Add a standard NIC and RDMA/iWARP driver for NetEffect 1/10Gb ethernet adapters.
Signed-off-by: Glenn Streiff <gstreiff@neteffect.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
L: linux-scsi@vger.kernel.org
S: Maintained
+NETEFFECT IWARP RNIC DRIVER (IW_NES)
+P: Faisal Latif
+M: flatif@neteffect.com
+P: Glenn Streiff
+M: gstreiff@neteffect.com
+L: general@lists.openfabrics.org
+W: http://www.neteffect.com
+S: Supported
+F: drivers/infiniband/hw/nes/
+
NETEM NETWORK EMULATOR
P: Stephen Hemminger
M: shemminger@linux-foundation.org
source "drivers/infiniband/hw/ehca/Kconfig"
source "drivers/infiniband/hw/amso1100/Kconfig"
source "drivers/infiniband/hw/cxgb3/Kconfig"
-
source "drivers/infiniband/hw/mlx4/Kconfig"
+source "drivers/infiniband/hw/nes/Kconfig"
source "drivers/infiniband/ulp/ipoib/Kconfig"
obj-$(CONFIG_INFINIBAND_AMSO1100) += hw/amso1100/
obj-$(CONFIG_INFINIBAND_CXGB3) += hw/cxgb3/
obj-$(CONFIG_MLX4_INFINIBAND) += hw/mlx4/
+obj-$(CONFIG_INFINIBAND_NES) += hw/nes/
obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/
obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/
obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/
--- /dev/null
+config INFINIBAND_NES
+ tristate "NetEffect RNIC Driver"
+ depends on PCI && INET && INFINIBAND
+ select LIBCRC32C
+ ---help---
+ This is a low-level driver for NetEffect RDMA enabled
+ Network Interface Cards (RNIC).
+
+config INFINIBAND_NES_DEBUG
+ bool "Verbose debugging output"
+ depends on INFINIBAND_NES
+ default n
+ ---help---
+ This option causes the NetEffect RNIC driver to produce debug
+ messages. Select this if you are developing the driver
+ or trying to diagnose a problem.
--- /dev/null
+obj-$(CONFIG_INFINIBAND_NES) += iw_nes.o
+
+iw_nes-objs := nes.o nes_hw.o nes_nic.o nes_utils.o nes_verbs.o nes_cm.o
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <linux/highmem.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <rdma/ib_smi.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_pack.h>
+#include <rdma/iw_cm.h>
+
+#include "nes.h"
+
+#include <net/netevent.h>
+#include <net/neighbour.h>
+#include <linux/route.h>
+#include <net/ip_fib.h>
+
+MODULE_AUTHOR("NetEffect");
+MODULE_DESCRIPTION("NetEffect RNIC Low-level iWARP Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRV_VERSION);
+
+int max_mtu = 9000;
+int nics_per_function = 1;
+int interrupt_mod_interval = 0;
+
+
+/* Interoperability */
+int mpa_version = 1;
+module_param(mpa_version, int, 0);
+MODULE_PARM_DESC(mpa_version, "MPA version to be used int MPA Req/Resp (0 or 1)");
+
+/* Interoperability */
+int disable_mpa_crc = 0;
+module_param(disable_mpa_crc, int, 0);
+MODULE_PARM_DESC(disable_mpa_crc, "Disable checking of MPA CRC");
+
+unsigned int send_first = 0;
+module_param(send_first, int, 0);
+MODULE_PARM_DESC(send_first, "Send RDMA Message First on Active Connection");
+
+
+unsigned int nes_drv_opt = 0;
+module_param(nes_drv_opt, int, 0);
+MODULE_PARM_DESC(nes_drv_opt, "Driver option parameters");
+
+unsigned int nes_debug_level = 0;
+module_param_named(debug_level, nes_debug_level, uint, 0644);
+MODULE_PARM_DESC(debug_level, "Enable debug output level");
+
+LIST_HEAD(nes_adapter_list);
+LIST_HEAD(nes_dev_list);
+
+atomic_t qps_destroyed;
+atomic_t cqp_reqs_allocated;
+atomic_t cqp_reqs_freed;
+atomic_t cqp_reqs_dynallocated;
+atomic_t cqp_reqs_dynfreed;
+atomic_t cqp_reqs_queued;
+atomic_t cqp_reqs_redriven;
+
+static void nes_print_macaddr(struct net_device *netdev);
+static irqreturn_t nes_interrupt(int, void *);
+static int __devinit nes_probe(struct pci_dev *, const struct pci_device_id *);
+static void __devexit nes_remove(struct pci_dev *);
+static int __init nes_init_module(void);
+static void __exit nes_exit_module(void);
+static unsigned int ee_flsh_adapter;
+static unsigned int sysfs_nonidx_addr;
+static unsigned int sysfs_idx_addr;
+
+static struct pci_device_id nes_pci_table[] = {
+ {PCI_VENDOR_ID_NETEFFECT, PCI_DEVICE_ID_NETEFFECT_NE020, PCI_ANY_ID, PCI_ANY_ID},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, nes_pci_table);
+
+static int nes_inetaddr_event(struct notifier_block *, unsigned long, void *);
+static int nes_net_event(struct notifier_block *, unsigned long, void *);
+static int nes_notifiers_registered;
+
+
+static struct notifier_block nes_inetaddr_notifier = {
+ .notifier_call = nes_inetaddr_event
+};
+
+static struct notifier_block nes_net_notifier = {
+ .notifier_call = nes_net_event
+};
+
+
+
+
+/**
+ * nes_inetaddr_event
+ */
+static int nes_inetaddr_event(struct notifier_block *notifier,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *ifa = ptr;
+ struct net_device *event_netdev = ifa->ifa_dev->dev;
+ struct nes_device *nesdev;
+ struct net_device *netdev;
+ struct nes_vnic *nesvnic;
+ unsigned int addr;
+ unsigned int mask;
+
+ addr = ntohl(ifa->ifa_address);
+ mask = ntohl(ifa->ifa_mask);
+ nes_debug(NES_DBG_NETDEV, "nes_inetaddr_event: ip address %08X, netmask %08X.\n",
+ addr, mask);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ nes_debug(NES_DBG_NETDEV, "Nesdev list entry = 0x%p. (%s)\n",
+ nesdev, nesdev->netdev[0]->name);
+ netdev = nesdev->netdev[0];
+ nesvnic = netdev_priv(netdev);
+ if (netdev == event_netdev) {
+ if (nesvnic->rdma_enabled == 0) {
+ nes_debug(NES_DBG_NETDEV, "Returning without processing event for %s since"
+ " RDMA is not enabled.\n",
+ netdev->name);
+ return NOTIFY_OK;
+ }
+ /* we have ifa->ifa_address/mask here if we need it */
+ switch (event) {
+ case NETDEV_DOWN:
+ nes_debug(NES_DBG_NETDEV, "event:DOWN\n");
+ nes_write_indexed(nesdev,
+ NES_IDX_DST_IP_ADDR+(0x10*PCI_FUNC(nesdev->pcidev->devfn)), 0);
+
+ nes_manage_arp_cache(netdev, netdev->dev_addr,
+ ntohl(nesvnic->local_ipaddr), NES_ARP_DELETE);
+ nesvnic->local_ipaddr = 0;
+ return NOTIFY_OK;
+ break;
+ case NETDEV_UP:
+ nes_debug(NES_DBG_NETDEV, "event:UP\n");
+
+ if (nesvnic->local_ipaddr != 0) {
+ nes_debug(NES_DBG_NETDEV, "Interface already has local_ipaddr\n");
+ return NOTIFY_OK;
+ }
+ /* Add the address to the IP table */
+ nesvnic->local_ipaddr = ifa->ifa_address;
+
+ nes_write_indexed(nesdev,
+ NES_IDX_DST_IP_ADDR+(0x10*PCI_FUNC(nesdev->pcidev->devfn)),
+ ntohl(ifa->ifa_address));
+ nes_manage_arp_cache(netdev, netdev->dev_addr,
+ ntohl(nesvnic->local_ipaddr), NES_ARP_ADD);
+ return NOTIFY_OK;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+/**
+ * nes_net_event
+ */
+static int nes_net_event(struct notifier_block *notifier,
+ unsigned long event, void *ptr)
+{
+ struct neighbour *neigh = ptr;
+ struct nes_device *nesdev;
+ struct net_device *netdev;
+ struct nes_vnic *nesvnic;
+
+ switch (event) {
+ case NETEVENT_NEIGH_UPDATE:
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ /* nes_debug(NES_DBG_NETDEV, "Nesdev list entry = 0x%p.\n", nesdev); */
+ netdev = nesdev->netdev[0];
+ nesvnic = netdev_priv(netdev);
+ if (netdev == neigh->dev) {
+ if (nesvnic->rdma_enabled == 0) {
+ nes_debug(NES_DBG_NETDEV, "Skipping device %s since no RDMA\n",
+ netdev->name);
+ } else {
+ if (neigh->nud_state & NUD_VALID) {
+ nes_manage_arp_cache(neigh->dev, neigh->ha,
+ ntohl(*(__be32 *)neigh->primary_key), NES_ARP_ADD);
+ } else {
+ nes_manage_arp_cache(neigh->dev, neigh->ha,
+ ntohl(*(__be32 *)neigh->primary_key), NES_ARP_DELETE);
+ }
+ }
+ return NOTIFY_OK;
+ }
+ }
+ break;
+ default:
+ nes_debug(NES_DBG_NETDEV, "NETEVENT_ %lu undefined\n", event);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+/**
+ * nes_add_ref
+ */
+void nes_add_ref(struct ib_qp *ibqp)
+{
+ struct nes_qp *nesqp;
+
+ nesqp = to_nesqp(ibqp);
+ nes_debug(NES_DBG_QP, "Bumping refcount for QP%u. Pre-inc value = %u\n",
+ ibqp->qp_num, atomic_read(&nesqp->refcount));
+ atomic_inc(&nesqp->refcount);
+}
+
+static void nes_cqp_rem_ref_callback(struct nes_device *nesdev, struct nes_cqp_request *cqp_request)
+{
+ unsigned long flags;
+ struct nes_qp *nesqp = cqp_request->cqp_callback_pointer;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 qp_id;
+
+ atomic_inc(&qps_destroyed);
+
+ /* Free the control structures */
+
+ qp_id = nesqp->hwqp.qp_id;
+ if (nesqp->pbl_vbase) {
+ pci_free_consistent(nesdev->pcidev, nesqp->qp_mem_size,
+ nesqp->hwqp.q2_vbase, nesqp->hwqp.q2_pbase);
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ nesadapter->free_256pbl++;
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ pci_free_consistent(nesdev->pcidev, 256, nesqp->pbl_vbase, nesqp->pbl_pbase);
+ nesqp->pbl_vbase = NULL;
+
+ } else {
+ pci_free_consistent(nesdev->pcidev, nesqp->qp_mem_size,
+ nesqp->hwqp.sq_vbase, nesqp->hwqp.sq_pbase);
+ }
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, nesqp->hwqp.qp_id);
+
+ kfree(nesqp->allocated_buffer);
+
+}
+
+/**
+ * nes_rem_ref
+ */
+void nes_rem_ref(struct ib_qp *ibqp)
+{
+ u64 u64temp;
+ struct nes_qp *nesqp;
+ struct nes_vnic *nesvnic = to_nesvnic(ibqp->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ u32 opcode;
+
+ nesqp = to_nesqp(ibqp);
+
+ if (atomic_read(&nesqp->refcount) == 0) {
+ printk(KERN_INFO PFX "%s: Reference count already 0 for QP%d, last aeq = 0x%04X.\n",
+ __FUNCTION__, ibqp->qp_num, nesqp->last_aeq);
+ BUG();
+ }
+
+ if (atomic_dec_and_test(&nesqp->refcount)) {
+ nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = NULL;
+
+ /* Destroy the QP */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_QP, "Failed to get a cqp_request.\n");
+ return;
+ }
+ cqp_request->waiting = 0;
+ cqp_request->callback = 1;
+ cqp_request->cqp_callback = nes_cqp_rem_ref_callback;
+ cqp_request->cqp_callback_pointer = nesqp;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ opcode = NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_IWARP;
+
+ if (nesqp->hte_added) {
+ opcode |= NES_CQP_QP_DEL_HTE;
+ nesqp->hte_added = 0;
+ }
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+ u64temp = (u64)nesqp->nesqp_context_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+ }
+}
+
+
+/**
+ * nes_get_qp
+ */
+struct ib_qp *nes_get_qp(struct ib_device *device, int qpn)
+{
+ struct nes_vnic *nesvnic = to_nesvnic(device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+
+ if ((qpn < NES_FIRST_QPN) || (qpn >= (NES_FIRST_QPN + nesadapter->max_qp)))
+ return NULL;
+
+ return &nesadapter->qp_table[qpn - NES_FIRST_QPN]->ibqp;
+}
+
+
+/**
+ * nes_print_macaddr
+ */
+static void nes_print_macaddr(struct net_device *netdev)
+{
+ nes_debug(NES_DBG_INIT, "%s: MAC %02X:%02X:%02X:%02X:%02X:%02X, IRQ %u\n",
+ netdev->name,
+ netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
+ netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5],
+ netdev->irq);
+}
+
+
+/**
+ * nes_interrupt - handle interrupts
+ */
+static irqreturn_t nes_interrupt(int irq, void *dev_id)
+{
+ struct nes_device *nesdev = (struct nes_device *)dev_id;
+ int handled = 0;
+ u32 int_mask;
+ u32 int_req;
+ u32 int_stat;
+ u32 intf_int_stat;
+ u32 timer_stat;
+
+ if (nesdev->msi_enabled) {
+ /* No need to read the interrupt pending register if msi is enabled */
+ handled = 1;
+ } else {
+ if (unlikely(nesdev->nesadapter->hw_rev == NE020_REV)) {
+ /* Master interrupt enable provides synchronization for kicking off bottom half
+ when interrupt sharing is going on */
+ int_mask = nes_read32(nesdev->regs + NES_INT_MASK);
+ if (int_mask & 0x80000000) {
+ /* Check interrupt status to see if this might be ours */
+ int_stat = nes_read32(nesdev->regs + NES_INT_STAT);
+ int_req = nesdev->int_req;
+ if (int_stat&int_req) {
+ /* if interesting CEQ or AEQ is pending, claim the interrupt */
+ if ((int_stat&int_req) & (~(NES_INT_TIMER|NES_INT_INTF))) {
+ handled = 1;
+ } else {
+ if (((int_stat & int_req) & NES_INT_TIMER) == NES_INT_TIMER) {
+ /* Timer might be running but might be for another function */
+ timer_stat = nes_read32(nesdev->regs + NES_TIMER_STAT);
+ if ((timer_stat & nesdev->timer_int_req) != 0) {
+ handled = 1;
+ }
+ }
+ if ((((int_stat & int_req) & NES_INT_INTF) == NES_INT_INTF) &&
+ (handled == 0)) {
+ intf_int_stat = nes_read32(nesdev->regs+NES_INTF_INT_STAT);
+ if ((intf_int_stat & nesdev->intf_int_req) != 0) {
+ handled = 1;
+ }
+ }
+ }
+ if (handled) {
+ nes_write32(nesdev->regs+NES_INT_MASK, int_mask & (~0x80000000));
+ int_mask = nes_read32(nesdev->regs+NES_INT_MASK);
+ /* Save off the status to save an additional read */
+ nesdev->int_stat = int_stat;
+ nesdev->napi_isr_ran = 1;
+ }
+ }
+ }
+ } else {
+ handled = nes_read32(nesdev->regs+NES_INT_PENDING);
+ }
+ }
+
+ if (handled) {
+
+ if (nes_napi_isr(nesdev) == 0) {
+ tasklet_schedule(&nesdev->dpc_tasklet);
+
+ }
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+
+/**
+ * nes_probe - Device initialization
+ */
+static int __devinit nes_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
+{
+ struct net_device *netdev = NULL;
+ struct nes_device *nesdev = NULL;
+ int ret = 0;
+ struct nes_vnic *nesvnic = NULL;
+ void __iomem *mmio_regs = NULL;
+ u8 hw_rev;
+
+ assert(pcidev != NULL);
+ assert(ent != NULL);
+
+ printk(KERN_INFO PFX "NetEffect RNIC driver v%s loading. (%s)\n",
+ DRV_VERSION, pci_name(pcidev));
+
+ ret = pci_enable_device(pcidev);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to enable PCI device. (%s)\n", pci_name(pcidev));
+ goto bail0;
+ }
+
+ nes_debug(NES_DBG_INIT, "BAR0 (@0x%08lX) size = 0x%lX bytes\n",
+ (long unsigned int)pci_resource_start(pcidev, BAR_0),
+ (long unsigned int)pci_resource_len(pcidev, BAR_0));
+ nes_debug(NES_DBG_INIT, "BAR1 (@0x%08lX) size = 0x%lX bytes\n",
+ (long unsigned int)pci_resource_start(pcidev, BAR_1),
+ (long unsigned int)pci_resource_len(pcidev, BAR_1));
+
+ /* Make sure PCI base addr are MMIO */
+ if (!(pci_resource_flags(pcidev, BAR_0) & IORESOURCE_MEM) ||
+ !(pci_resource_flags(pcidev, BAR_1) & IORESOURCE_MEM)) {
+ printk(KERN_ERR PFX "PCI regions not an MMIO resource\n");
+ ret = -ENODEV;
+ goto bail1;
+ }
+
+ /* Reserve PCI I/O and memory resources */
+ ret = pci_request_regions(pcidev, DRV_NAME);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to request regions. (%s)\n", pci_name(pcidev));
+ goto bail1;
+ }
+
+ if ((sizeof(dma_addr_t) > 4)) {
+ ret = pci_set_dma_mask(pcidev, DMA_64BIT_MASK);
+ if (ret < 0) {
+ printk(KERN_ERR PFX "64b DMA mask configuration failed\n");
+ goto bail2;
+ }
+ ret = pci_set_consistent_dma_mask(pcidev, DMA_64BIT_MASK);
+ if (ret) {
+ printk(KERN_ERR PFX "64b DMA consistent mask configuration failed\n");
+ goto bail2;
+ }
+ } else {
+ ret = pci_set_dma_mask(pcidev, DMA_32BIT_MASK);
+ if (ret < 0) {
+ printk(KERN_ERR PFX "32b DMA mask configuration failed\n");
+ goto bail2;
+ }
+ ret = pci_set_consistent_dma_mask(pcidev, DMA_32BIT_MASK);
+ if (ret) {
+ printk(KERN_ERR PFX "32b DMA consistent mask configuration failed\n");
+ goto bail2;
+ }
+ }
+
+ pci_set_master(pcidev);
+
+ /* Allocate hardware structure */
+ nesdev = kzalloc(sizeof(struct nes_device), GFP_KERNEL);
+ if (!nesdev) {
+ printk(KERN_ERR PFX "%s: Unable to alloc hardware struct\n", pci_name(pcidev));
+ ret = -ENOMEM;
+ goto bail2;
+ }
+
+ nes_debug(NES_DBG_INIT, "Allocated nes device at %p\n", nesdev);
+ nesdev->pcidev = pcidev;
+ pci_set_drvdata(pcidev, nesdev);
+
+ pci_read_config_byte(pcidev, 0x0008, &hw_rev);
+ nes_debug(NES_DBG_INIT, "hw_rev=%u\n", hw_rev);
+
+ spin_lock_init(&nesdev->indexed_regs_lock);
+
+ /* Remap the PCI registers in adapter BAR0 to kernel VA space */
+ mmio_regs = ioremap_nocache(pci_resource_start(pcidev, BAR_0), sizeof(mmio_regs));
+ if (mmio_regs == NULL) {
+ printk(KERN_ERR PFX "Unable to remap BAR0\n");
+ ret = -EIO;
+ goto bail3;
+ }
+ nesdev->regs = mmio_regs;
+ nesdev->index_reg = 0x50 + (PCI_FUNC(pcidev->devfn)*8) + mmio_regs;
+
+ /* Ensure interrupts are disabled */
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x7fffffff);
+
+ if (nes_drv_opt & NES_DRV_OPT_ENABLE_MSI) {
+ if (!pci_enable_msi(nesdev->pcidev)) {
+ nesdev->msi_enabled = 1;
+ nes_debug(NES_DBG_INIT, "MSI is enabled for device %s\n",
+ pci_name(pcidev));
+ } else {
+ nes_debug(NES_DBG_INIT, "MSI is disabled by linux for device %s\n",
+ pci_name(pcidev));
+ }
+ } else {
+ nes_debug(NES_DBG_INIT, "MSI not requested due to driver options for device %s\n",
+ pci_name(pcidev));
+ }
+
+ nesdev->csr_start = pci_resource_start(nesdev->pcidev, BAR_0);
+ nesdev->doorbell_region = pci_resource_start(nesdev->pcidev, BAR_1);
+
+ /* Init the adapter */
+ nesdev->nesadapter = nes_init_adapter(nesdev, hw_rev);
+ nesdev->nesadapter->et_rx_coalesce_usecs_irq = interrupt_mod_interval;
+ if (!nesdev->nesadapter) {
+ printk(KERN_ERR PFX "Unable to initialize adapter.\n");
+ ret = -ENOMEM;
+ goto bail5;
+ }
+
+ /* nesdev->base_doorbell_index =
+ nesdev->nesadapter->pd_config_base[PCI_FUNC(nesdev->pcidev->devfn)]; */
+ nesdev->base_doorbell_index = 1;
+ nesdev->doorbell_start = nesdev->nesadapter->doorbell_start;
+ nesdev->mac_index = PCI_FUNC(nesdev->pcidev->devfn) % nesdev->nesadapter->port_count;
+
+ tasklet_init(&nesdev->dpc_tasklet, nes_dpc, (unsigned long)nesdev);
+
+ /* bring up the Control QP */
+ if (nes_init_cqp(nesdev)) {
+ ret = -ENODEV;
+ goto bail6;
+ }
+
+ /* Arm the CCQ */
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ PCI_FUNC(nesdev->pcidev->devfn));
+ nes_read32(nesdev->regs+NES_CQE_ALLOC);
+
+ /* Enable the interrupts */
+ nesdev->int_req = (0x101 << PCI_FUNC(nesdev->pcidev->devfn)) |
+ (1 << (PCI_FUNC(nesdev->pcidev->devfn)+16));
+ if (PCI_FUNC(nesdev->pcidev->devfn) < 4) {
+ nesdev->int_req |= (1 << (PCI_FUNC(nesdev->pcidev->devfn)+24));
+ }
+
+ /* TODO: This really should be the first driver to load, not function 0 */
+ if (PCI_FUNC(nesdev->pcidev->devfn) == 0) {
+ /* pick up PCI and critical errors if the first driver to load */
+ nesdev->intf_int_req = NES_INTF_INT_PCIERR | NES_INTF_INT_CRITERR;
+ nesdev->int_req |= NES_INT_INTF;
+ } else {
+ nesdev->intf_int_req = 0;
+ }
+ nesdev->intf_int_req |= (1 << (PCI_FUNC(nesdev->pcidev->devfn)+16));
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_MASKS0, 0);
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_MASKS1, 0);
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_MASKS2, 0x00001265);
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_MASKS4, 0x18021804);
+
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_MASKS3, 0x17801790);
+
+ /* deal with both periodic and one_shot */
+ nesdev->timer_int_req = 0x101 << PCI_FUNC(nesdev->pcidev->devfn);
+ nesdev->nesadapter->timer_int_req |= nesdev->timer_int_req;
+ nes_debug(NES_DBG_INIT, "setting int_req for function %u, nesdev = 0x%04X, adapter = 0x%04X\n",
+ PCI_FUNC(nesdev->pcidev->devfn),
+ nesdev->timer_int_req, nesdev->nesadapter->timer_int_req);
+
+ nes_write32(nesdev->regs+NES_INTF_INT_MASK, ~(nesdev->intf_int_req));
+
+ list_add_tail(&nesdev->list, &nes_dev_list);
+
+ /* Request an interrupt line for the driver */
+ ret = request_irq(pcidev->irq, nes_interrupt, IRQF_SHARED, DRV_NAME, nesdev);
+ if (ret) {
+ printk(KERN_ERR PFX "%s: requested IRQ %u is busy\n",
+ pci_name(pcidev), pcidev->irq);
+ goto bail65;
+ }
+
+ nes_write32(nesdev->regs+NES_INT_MASK, ~nesdev->int_req);
+
+ if (nes_notifiers_registered == 0) {
+ register_inetaddr_notifier(&nes_inetaddr_notifier);
+ register_netevent_notifier(&nes_net_notifier);
+ }
+ nes_notifiers_registered++;
+
+ /* Initialize network devices */
+ if ((netdev = nes_netdev_init(nesdev, mmio_regs)) == NULL) {
+ goto bail7;
+ }
+
+ /* Register network device */
+ ret = register_netdev(netdev);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to register netdev, ret = %d\n", ret);
+ nes_netdev_destroy(netdev);
+ goto bail7;
+ }
+
+ nes_print_macaddr(netdev);
+ /* create a CM core for this netdev */
+ nesvnic = netdev_priv(netdev);
+
+ nesdev->netdev_count++;
+ nesdev->nesadapter->netdev_count++;
+
+
+ printk(KERN_ERR PFX "%s: NetEffect RNIC driver successfully loaded.\n",
+ pci_name(pcidev));
+ return 0;
+
+ bail7:
+ printk(KERN_ERR PFX "bail7\n");
+ while (nesdev->netdev_count > 0) {
+ nesdev->netdev_count--;
+ nesdev->nesadapter->netdev_count--;
+
+ unregister_netdev(nesdev->netdev[nesdev->netdev_count]);
+ nes_netdev_destroy(nesdev->netdev[nesdev->netdev_count]);
+ }
+
+ nes_debug(NES_DBG_INIT, "netdev_count=%d, nesadapter->netdev_count=%d\n",
+ nesdev->netdev_count, nesdev->nesadapter->netdev_count);
+
+ nes_notifiers_registered--;
+ if (nes_notifiers_registered == 0) {
+ unregister_netevent_notifier(&nes_net_notifier);
+ unregister_inetaddr_notifier(&nes_inetaddr_notifier);
+ }
+
+ list_del(&nesdev->list);
+ nes_destroy_cqp(nesdev);
+
+ bail65:
+ printk(KERN_ERR PFX "bail65\n");
+ free_irq(pcidev->irq, nesdev);
+ if (nesdev->msi_enabled) {
+ pci_disable_msi(pcidev);
+ }
+ bail6:
+ printk(KERN_ERR PFX "bail6\n");
+ tasklet_kill(&nesdev->dpc_tasklet);
+ /* Deallocate the Adapter Structure */
+ nes_destroy_adapter(nesdev->nesadapter);
+
+ bail5:
+ printk(KERN_ERR PFX "bail5\n");
+ iounmap(nesdev->regs);
+
+ bail3:
+ printk(KERN_ERR PFX "bail3\n");
+ kfree(nesdev);
+
+ bail2:
+ pci_release_regions(pcidev);
+
+ bail1:
+ pci_disable_device(pcidev);
+
+ bail0:
+ return ret;
+}
+
+
+/**
+ * nes_remove - unload from kernel
+ */
+static void __devexit nes_remove(struct pci_dev *pcidev)
+{
+ struct nes_device *nesdev = pci_get_drvdata(pcidev);
+ struct net_device *netdev;
+ int netdev_index = 0;
+
+ if (nesdev->netdev_count) {
+ netdev = nesdev->netdev[netdev_index];
+ if (netdev) {
+ netif_stop_queue(netdev);
+ unregister_netdev(netdev);
+ nes_netdev_destroy(netdev);
+
+ nesdev->netdev[netdev_index] = NULL;
+ nesdev->netdev_count--;
+ nesdev->nesadapter->netdev_count--;
+ }
+ }
+
+ nes_notifiers_registered--;
+ if (nes_notifiers_registered == 0) {
+ unregister_netevent_notifier(&nes_net_notifier);
+ unregister_inetaddr_notifier(&nes_inetaddr_notifier);
+ }
+
+ list_del(&nesdev->list);
+ nes_destroy_cqp(nesdev);
+ tasklet_kill(&nesdev->dpc_tasklet);
+
+ /* Deallocate the Adapter Structure */
+ nes_destroy_adapter(nesdev->nesadapter);
+
+ free_irq(pcidev->irq, nesdev);
+
+ if (nesdev->msi_enabled) {
+ pci_disable_msi(pcidev);
+ }
+
+ iounmap(nesdev->regs);
+ kfree(nesdev);
+
+ /* nes_debug(NES_DBG_SHUTDOWN, "calling pci_release_regions.\n"); */
+ pci_release_regions(pcidev);
+ pci_disable_device(pcidev);
+ pci_set_drvdata(pcidev, NULL);
+}
+
+
+static struct pci_driver nes_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = nes_pci_table,
+ .probe = nes_probe,
+ .remove = __devexit_p(nes_remove),
+};
+
+static ssize_t nes_show_adapter(struct device_driver *ddp, char *buf)
+{
+ unsigned int devfn = 0xffffffff;
+ unsigned char bus_number = 0xff;
+ unsigned int i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ devfn = nesdev->nesadapter->devfn;
+ bus_number = nesdev->nesadapter->bus_number;
+ break;
+ }
+ i++;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%x:%x", bus_number, devfn);
+}
+
+static ssize_t nes_store_adapter(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+
+ ee_flsh_adapter = simple_strtoul(p, &p, 10);
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_ee_cmd(struct device_driver *ddp, char *buf)
+{
+ u32 eeprom_cmd = 0xdead;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ eeprom_cmd = nes_read32(nesdev->regs + NES_EEPROM_COMMAND);
+ break;
+ }
+ i++;
+ }
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", eeprom_cmd);
+}
+
+static ssize_t nes_store_ee_cmd(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ val = simple_strtoul(p, &p, 16);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nes_write32(nesdev->regs + NES_EEPROM_COMMAND, val);
+ break;
+ }
+ i++;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_ee_data(struct device_driver *ddp, char *buf)
+{
+ u32 eeprom_data = 0xdead;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ eeprom_data = nes_read32(nesdev->regs + NES_EEPROM_DATA);
+ break;
+ }
+ i++;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", eeprom_data);
+}
+
+static ssize_t nes_store_ee_data(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ val = simple_strtoul(p, &p, 16);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nes_write32(nesdev->regs + NES_EEPROM_DATA, val);
+ break;
+ }
+ i++;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_flash_cmd(struct device_driver *ddp, char *buf)
+{
+ u32 flash_cmd = 0xdead;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ flash_cmd = nes_read32(nesdev->regs + NES_FLASH_COMMAND);
+ break;
+ }
+ i++;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", flash_cmd);
+}
+
+static ssize_t nes_store_flash_cmd(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ val = simple_strtoul(p, &p, 16);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nes_write32(nesdev->regs + NES_FLASH_COMMAND, val);
+ break;
+ }
+ i++;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_flash_data(struct device_driver *ddp, char *buf)
+{
+ u32 flash_data = 0xdead;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ flash_data = nes_read32(nesdev->regs + NES_FLASH_DATA);
+ break;
+ }
+ i++;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", flash_data);
+}
+
+static ssize_t nes_store_flash_data(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ val = simple_strtoul(p, &p, 16);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nes_write32(nesdev->regs + NES_FLASH_DATA, val);
+ break;
+ }
+ i++;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_nonidx_addr(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", sysfs_nonidx_addr);
+}
+
+static ssize_t nes_store_nonidx_addr(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X')
+ sysfs_nonidx_addr = simple_strtoul(p, &p, 16);
+
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_nonidx_data(struct device_driver *ddp, char *buf)
+{
+ u32 nonidx_data = 0xdead;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nonidx_data = nes_read32(nesdev->regs + sysfs_nonidx_addr);
+ break;
+ }
+ i++;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", nonidx_data);
+}
+
+static ssize_t nes_store_nonidx_data(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ val = simple_strtoul(p, &p, 16);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nes_write32(nesdev->regs + sysfs_nonidx_addr, val);
+ break;
+ }
+ i++;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_idx_addr(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", sysfs_idx_addr);
+}
+
+static ssize_t nes_store_idx_addr(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X')
+ sysfs_idx_addr = simple_strtoul(p, &p, 16);
+
+ return strnlen(buf, count);
+}
+
+static ssize_t nes_show_idx_data(struct device_driver *ddp, char *buf)
+{
+ u32 idx_data = 0xdead;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ idx_data = nes_read_indexed(nesdev, sysfs_idx_addr);
+ break;
+ }
+ i++;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", idx_data);
+}
+
+static ssize_t nes_store_idx_data(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+ u32 i = 0;
+ struct nes_device *nesdev;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ val = simple_strtoul(p, &p, 16);
+ list_for_each_entry(nesdev, &nes_dev_list, list) {
+ if (i == ee_flsh_adapter) {
+ nes_write_indexed(nesdev, sysfs_idx_addr, val);
+ break;
+ }
+ i++;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static DRIVER_ATTR(adapter, S_IRUSR | S_IWUSR,
+ nes_show_adapter, nes_store_adapter);
+static DRIVER_ATTR(eeprom_cmd, S_IRUSR | S_IWUSR,
+ nes_show_ee_cmd, nes_store_ee_cmd);
+static DRIVER_ATTR(eeprom_data, S_IRUSR | S_IWUSR,
+ nes_show_ee_data, nes_store_ee_data);
+static DRIVER_ATTR(flash_cmd, S_IRUSR | S_IWUSR,
+ nes_show_flash_cmd, nes_store_flash_cmd);
+static DRIVER_ATTR(flash_data, S_IRUSR | S_IWUSR,
+ nes_show_flash_data, nes_store_flash_data);
+static DRIVER_ATTR(nonidx_addr, S_IRUSR | S_IWUSR,
+ nes_show_nonidx_addr, nes_store_nonidx_addr);
+static DRIVER_ATTR(nonidx_data, S_IRUSR | S_IWUSR,
+ nes_show_nonidx_data, nes_store_nonidx_data);
+static DRIVER_ATTR(idx_addr, S_IRUSR | S_IWUSR,
+ nes_show_idx_addr, nes_store_idx_addr);
+static DRIVER_ATTR(idx_data, S_IRUSR | S_IWUSR,
+ nes_show_idx_data, nes_store_idx_data);
+
+static int nes_create_driver_sysfs(struct pci_driver *drv)
+{
+ int error;
+ error = driver_create_file(&drv->driver, &driver_attr_adapter);
+ error |= driver_create_file(&drv->driver, &driver_attr_eeprom_cmd);
+ error |= driver_create_file(&drv->driver, &driver_attr_eeprom_data);
+ error |= driver_create_file(&drv->driver, &driver_attr_flash_cmd);
+ error |= driver_create_file(&drv->driver, &driver_attr_flash_data);
+ error |= driver_create_file(&drv->driver, &driver_attr_nonidx_addr);
+ error |= driver_create_file(&drv->driver, &driver_attr_nonidx_data);
+ error |= driver_create_file(&drv->driver, &driver_attr_idx_addr);
+ error |= driver_create_file(&drv->driver, &driver_attr_idx_data);
+ return error;
+}
+
+static void nes_remove_driver_sysfs(struct pci_driver *drv)
+{
+ driver_remove_file(&drv->driver, &driver_attr_adapter);
+ driver_remove_file(&drv->driver, &driver_attr_eeprom_cmd);
+ driver_remove_file(&drv->driver, &driver_attr_eeprom_data);
+ driver_remove_file(&drv->driver, &driver_attr_flash_cmd);
+ driver_remove_file(&drv->driver, &driver_attr_flash_data);
+ driver_remove_file(&drv->driver, &driver_attr_nonidx_addr);
+ driver_remove_file(&drv->driver, &driver_attr_nonidx_data);
+ driver_remove_file(&drv->driver, &driver_attr_idx_addr);
+ driver_remove_file(&drv->driver, &driver_attr_idx_data);
+}
+
+/**
+ * nes_init_module - module initialization entry point
+ */
+static int __init nes_init_module(void)
+{
+ int retval;
+ int retval1;
+
+ retval = nes_cm_start();
+ if (retval) {
+ printk(KERN_ERR PFX "Unable to start NetEffect iWARP CM.\n");
+ return retval;
+ }
+ retval = pci_register_driver(&nes_pci_driver);
+ if (retval >= 0) {
+ retval1 = nes_create_driver_sysfs(&nes_pci_driver);
+ if (retval1 < 0)
+ printk(KERN_ERR PFX "Unable to create NetEffect sys files.\n");
+ }
+ return retval;
+}
+
+
+/**
+ * nes_exit_module - module unload entry point
+ */
+static void __exit nes_exit_module(void)
+{
+ nes_cm_stop();
+ nes_remove_driver_sysfs(&nes_pci_driver);
+
+ pci_unregister_driver(&nes_pci_driver);
+}
+
+
+module_init(nes_init_module);
+module_exit(nes_exit_module);
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __NES_H
+#define __NES_H
+
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <linux/crc32c.h>
+
+#include <rdma/ib_smi.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_pack.h>
+#include <rdma/rdma_cm.h>
+#include <rdma/iw_cm.h>
+
+#define NES_SEND_FIRST_WRITE
+
+#define QUEUE_DISCONNECTS
+
+#define DRV_BUILD "1"
+
+#define DRV_NAME "iw_nes"
+#define DRV_VERSION "1.0 KO Build " DRV_BUILD
+#define PFX DRV_NAME ": "
+
+/*
+ * NetEffect PCI vendor id and NE010 PCI device id.
+ */
+#ifndef PCI_VENDOR_ID_NETEFFECT /* not in pci.ids yet */
+#define PCI_VENDOR_ID_NETEFFECT 0x1678
+#define PCI_DEVICE_ID_NETEFFECT_NE020 0x0100
+#endif
+
+#define NE020_REV 4
+#define NE020_REV1 5
+
+#define BAR_0 0
+#define BAR_1 2
+
+#define RX_BUF_SIZE (1536 + 8)
+#define NES_REG0_SIZE (4 * 1024)
+#define NES_TX_TIMEOUT (6*HZ)
+#define NES_FIRST_QPN 64
+#define NES_SW_CONTEXT_ALIGN 1024
+
+#define NES_NIC_MAX_NICS 16
+#define NES_MAX_ARP_TABLE_SIZE 4096
+
+#define NES_NIC_CEQ_SIZE 8
+/* NICs will be on a separate CQ */
+#define NES_CCEQ_SIZE ((nesadapter->max_cq / nesadapter->port_count) - 32)
+
+#define NES_MAX_PORT_COUNT 4
+
+#define MAX_DPC_ITERATIONS 128
+
+#define NES_CQP_REQUEST_NO_DOORBELL_RING 0
+#define NES_CQP_REQUEST_RING_DOORBELL 1
+
+#define NES_DRV_OPT_ENABLE_MPA_VER_0 0x00000001
+#define NES_DRV_OPT_DISABLE_MPA_CRC 0x00000002
+#define NES_DRV_OPT_DISABLE_FIRST_WRITE 0x00000004
+#define NES_DRV_OPT_DISABLE_INTF 0x00000008
+#define NES_DRV_OPT_ENABLE_MSI 0x00000010
+#define NES_DRV_OPT_DUAL_LOGICAL_PORT 0x00000020
+#define NES_DRV_OPT_SUPRESS_OPTION_BC 0x00000040
+#define NES_DRV_OPT_NO_INLINE_DATA 0x00000080
+#define NES_DRV_OPT_DISABLE_INT_MOD 0x00000100
+#define NES_DRV_OPT_DISABLE_VIRT_WQ 0x00000200
+
+#define NES_AEQ_EVENT_TIMEOUT 2500
+#define NES_DISCONNECT_EVENT_TIMEOUT 2000
+
+/* debug levels */
+/* must match userspace */
+#define NES_DBG_HW 0x00000001
+#define NES_DBG_INIT 0x00000002
+#define NES_DBG_ISR 0x00000004
+#define NES_DBG_PHY 0x00000008
+#define NES_DBG_NETDEV 0x00000010
+#define NES_DBG_CM 0x00000020
+#define NES_DBG_CM1 0x00000040
+#define NES_DBG_NIC_RX 0x00000080
+#define NES_DBG_NIC_TX 0x00000100
+#define NES_DBG_CQP 0x00000200
+#define NES_DBG_MMAP 0x00000400
+#define NES_DBG_MR 0x00000800
+#define NES_DBG_PD 0x00001000
+#define NES_DBG_CQ 0x00002000
+#define NES_DBG_QP 0x00004000
+#define NES_DBG_MOD_QP 0x00008000
+#define NES_DBG_AEQ 0x00010000
+#define NES_DBG_IW_RX 0x00020000
+#define NES_DBG_IW_TX 0x00040000
+#define NES_DBG_SHUTDOWN 0x00080000
+#define NES_DBG_RSVD1 0x10000000
+#define NES_DBG_RSVD2 0x20000000
+#define NES_DBG_RSVD3 0x40000000
+#define NES_DBG_RSVD4 0x80000000
+#define NES_DBG_ALL 0xffffffff
+
+#ifdef CONFIG_INFINIBAND_NES_DEBUG
+#define nes_debug(level, fmt, args...) \
+ if (level & nes_debug_level) \
+ printk(KERN_ERR PFX "%s[%u]: " fmt, __FUNCTION__, __LINE__, ##args)
+
+#define assert(expr) \
+if (!(expr)) { \
+ printk(KERN_ERR PFX "Assertion failed! %s, %s, %s, line %d\n", \
+ #expr, __FILE__, __FUNCTION__, __LINE__); \
+}
+
+#define NES_EVENT_TIMEOUT 1200000
+#else
+#define nes_debug(level, fmt, args...)
+#define assert(expr) do {} while (0)
+
+#define NES_EVENT_TIMEOUT 100000
+#endif
+
+#include "nes_hw.h"
+#include "nes_verbs.h"
+#include "nes_context.h"
+#include "nes_user.h"
+#include "nes_cm.h"
+
+extern int max_mtu;
+extern int nics_per_function;
+#define max_frame_len (max_mtu+ETH_HLEN)
+extern int interrupt_mod_interval;
+extern int nes_if_count;
+extern int mpa_version;
+extern int disable_mpa_crc;
+extern unsigned int send_first;
+extern unsigned int nes_drv_opt;
+extern unsigned int nes_debug_level;
+
+extern struct list_head nes_adapter_list;
+extern struct list_head nes_dev_list;
+
+extern struct nes_cm_core *g_cm_core;
+
+extern atomic_t cm_connects;
+extern atomic_t cm_accepts;
+extern atomic_t cm_disconnects;
+extern atomic_t cm_closes;
+extern atomic_t cm_connecteds;
+extern atomic_t cm_connect_reqs;
+extern atomic_t cm_rejects;
+extern atomic_t mod_qp_timouts;
+extern atomic_t qps_created;
+extern atomic_t qps_destroyed;
+extern atomic_t sw_qps_destroyed;
+extern u32 mh_detected;
+extern u32 mh_pauses_sent;
+extern u32 cm_packets_sent;
+extern u32 cm_packets_bounced;
+extern u32 cm_packets_created;
+extern u32 cm_packets_received;
+extern u32 cm_packets_dropped;
+extern u32 cm_packets_retrans;
+extern u32 cm_listens_created;
+extern u32 cm_listens_destroyed;
+extern u32 cm_backlog_drops;
+extern atomic_t cm_loopbacks;
+extern atomic_t cm_nodes_created;
+extern atomic_t cm_nodes_destroyed;
+extern atomic_t cm_accel_dropped_pkts;
+extern atomic_t cm_resets_recvd;
+
+extern u32 crit_err_count;
+extern u32 int_mod_timer_init;
+extern u32 int_mod_cq_depth_256;
+extern u32 int_mod_cq_depth_128;
+extern u32 int_mod_cq_depth_32;
+extern u32 int_mod_cq_depth_24;
+extern u32 int_mod_cq_depth_16;
+extern u32 int_mod_cq_depth_4;
+extern u32 int_mod_cq_depth_1;
+
+extern atomic_t cqp_reqs_allocated;
+extern atomic_t cqp_reqs_freed;
+extern atomic_t cqp_reqs_dynallocated;
+extern atomic_t cqp_reqs_dynfreed;
+extern atomic_t cqp_reqs_queued;
+extern atomic_t cqp_reqs_redriven;
+
+
+struct nes_device {
+ struct nes_adapter *nesadapter;
+ void __iomem *regs;
+ void __iomem *index_reg;
+ struct pci_dev *pcidev;
+ struct net_device *netdev[NES_NIC_MAX_NICS];
+ u64 link_status_interrupts;
+ struct tasklet_struct dpc_tasklet;
+ spinlock_t indexed_regs_lock;
+ unsigned long csr_start;
+ unsigned long doorbell_region;
+ unsigned long doorbell_start;
+ unsigned long mac_tx_errors;
+ unsigned long mac_pause_frames_sent;
+ unsigned long mac_pause_frames_received;
+ unsigned long mac_rx_errors;
+ unsigned long mac_rx_crc_errors;
+ unsigned long mac_rx_symbol_err_frames;
+ unsigned long mac_rx_jabber_frames;
+ unsigned long mac_rx_oversized_frames;
+ unsigned long mac_rx_short_frames;
+ unsigned long port_rx_discards;
+ unsigned long port_tx_discards;
+ unsigned int mac_index;
+ unsigned int nes_stack_start;
+
+ /* Control Structures */
+ void *cqp_vbase;
+ dma_addr_t cqp_pbase;
+ u32 cqp_mem_size;
+ u8 ceq_index;
+ u8 nic_ceq_index;
+ struct nes_hw_cqp cqp;
+ struct nes_hw_cq ccq;
+ struct list_head cqp_avail_reqs;
+ struct list_head cqp_pending_reqs;
+ struct nes_cqp_request *nes_cqp_requests;
+
+ u32 int_req;
+ u32 int_stat;
+ u32 timer_int_req;
+ u32 timer_only_int_count;
+ u32 intf_int_req;
+ u32 last_mac_tx_pauses;
+ u32 last_used_chunks_tx;
+ struct list_head list;
+
+ u16 base_doorbell_index;
+ u16 currcq_count;
+ u16 deepcq_count;
+ u8 msi_enabled;
+ u8 netdev_count;
+ u8 napi_isr_ran;
+ u8 disable_rx_flow_control;
+ u8 disable_tx_flow_control;
+};
+
+
+static inline void
+set_wqe_64bit_value(__le32 *wqe_words, u32 index, u64 value)
+{
+ wqe_words[index] = cpu_to_le32((u32) ((unsigned long)value));
+ wqe_words[index + 1] = cpu_to_le32((u32)(upper_32_bits((unsigned long)value)));
+}
+
+static inline void
+set_wqe_32bit_value(__le32 *wqe_words, u32 index, u32 value)
+{
+ wqe_words[index] = cpu_to_le32(value);
+}
+
+static inline void
+nes_fill_init_cqp_wqe(struct nes_hw_cqp_wqe *cqp_wqe, struct nes_device *nesdev)
+{
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_COMP_CTX_LOW_IDX,
+ (u64)((unsigned long) &nesdev->cqp));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PBL_BLK_COUNT_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PBL_LEN_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_LEN_LOW_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PA_LOW_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PA_HIGH_IDX] = 0;
+}
+
+static inline void
+nes_fill_init_qp_wqe(struct nes_hw_qp_wqe *wqe, struct nes_qp *nesqp, u32 head)
+{
+ u32 value;
+ value = ((u32)((unsigned long) nesqp)) | head;
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_HIGH_IDX,
+ (u32)(upper_32_bits((unsigned long)(nesqp))));
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, value);
+}
+
+/* Read from memory-mapped device */
+static inline u32 nes_read_indexed(struct nes_device *nesdev, u32 reg_index)
+{
+ unsigned long flags;
+ void __iomem *addr = nesdev->index_reg;
+ u32 value;
+
+ spin_lock_irqsave(&nesdev->indexed_regs_lock, flags);
+
+ writel(reg_index, addr);
+ value = readl((void __iomem *)addr + 4);
+
+ spin_unlock_irqrestore(&nesdev->indexed_regs_lock, flags);
+ return value;
+}
+
+static inline u32 nes_read32(const void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static inline u16 nes_read16(const void __iomem *addr)
+{
+ return readw(addr);
+}
+
+static inline u8 nes_read8(const void __iomem *addr)
+{
+ return readb(addr);
+}
+
+/* Write to memory-mapped device */
+static inline void nes_write_indexed(struct nes_device *nesdev, u32 reg_index, u32 val)
+{
+ unsigned long flags;
+ void __iomem *addr = nesdev->index_reg;
+
+ spin_lock_irqsave(&nesdev->indexed_regs_lock, flags);
+
+ writel(reg_index, addr);
+ writel(val, (void __iomem *)addr + 4);
+
+ spin_unlock_irqrestore(&nesdev->indexed_regs_lock, flags);
+}
+
+static inline void nes_write32(void __iomem *addr, u32 val)
+{
+ writel(val, addr);
+}
+
+static inline void nes_write16(void __iomem *addr, u16 val)
+{
+ writew(val, addr);
+}
+
+static inline void nes_write8(void __iomem *addr, u8 val)
+{
+ writeb(val, addr);
+}
+
+
+
+static inline int nes_alloc_resource(struct nes_adapter *nesadapter,
+ unsigned long *resource_array, u32 max_resources,
+ u32 *req_resource_num, u32 *next)
+{
+ unsigned long flags;
+ u32 resource_num;
+
+ spin_lock_irqsave(&nesadapter->resource_lock, flags);
+
+ resource_num = find_next_zero_bit(resource_array, max_resources, *next);
+ if (resource_num >= max_resources) {
+ resource_num = find_first_zero_bit(resource_array, max_resources);
+ if (resource_num >= max_resources) {
+ printk(KERN_ERR PFX "%s: No available resourcess.\n", __FUNCTION__);
+ spin_unlock_irqrestore(&nesadapter->resource_lock, flags);
+ return -EMFILE;
+ }
+ }
+ set_bit(resource_num, resource_array);
+ *next = resource_num+1;
+ if (*next == max_resources) {
+ *next = 0;
+ }
+ spin_unlock_irqrestore(&nesadapter->resource_lock, flags);
+ *req_resource_num = resource_num;
+
+ return 0;
+}
+
+static inline int nes_is_resource_allocated(struct nes_adapter *nesadapter,
+ unsigned long *resource_array, u32 resource_num)
+{
+ unsigned long flags;
+ int bit_is_set;
+
+ spin_lock_irqsave(&nesadapter->resource_lock, flags);
+
+ bit_is_set = test_bit(resource_num, resource_array);
+ nes_debug(NES_DBG_HW, "resource_num %u is%s allocated.\n",
+ resource_num, (bit_is_set ? "": " not"));
+ spin_unlock_irqrestore(&nesadapter->resource_lock, flags);
+
+ return bit_is_set;
+}
+
+static inline void nes_free_resource(struct nes_adapter *nesadapter,
+ unsigned long *resource_array, u32 resource_num)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nesadapter->resource_lock, flags);
+ clear_bit(resource_num, resource_array);
+ spin_unlock_irqrestore(&nesadapter->resource_lock, flags);
+}
+
+static inline struct nes_vnic *to_nesvnic(struct ib_device *ibdev)
+{
+ return container_of(ibdev, struct nes_ib_device, ibdev)->nesvnic;
+}
+
+static inline struct nes_pd *to_nespd(struct ib_pd *ibpd)
+{
+ return container_of(ibpd, struct nes_pd, ibpd);
+}
+
+static inline struct nes_ucontext *to_nesucontext(struct ib_ucontext *ibucontext)
+{
+ return container_of(ibucontext, struct nes_ucontext, ibucontext);
+}
+
+static inline struct nes_mr *to_nesmr(struct ib_mr *ibmr)
+{
+ return container_of(ibmr, struct nes_mr, ibmr);
+}
+
+static inline struct nes_mr *to_nesmr_from_ibfmr(struct ib_fmr *ibfmr)
+{
+ return container_of(ibfmr, struct nes_mr, ibfmr);
+}
+
+static inline struct nes_mr *to_nesmw(struct ib_mw *ibmw)
+{
+ return container_of(ibmw, struct nes_mr, ibmw);
+}
+
+static inline struct nes_fmr *to_nesfmr(struct nes_mr *nesmr)
+{
+ return container_of(nesmr, struct nes_fmr, nesmr);
+}
+
+static inline struct nes_cq *to_nescq(struct ib_cq *ibcq)
+{
+ return container_of(ibcq, struct nes_cq, ibcq);
+}
+
+static inline struct nes_qp *to_nesqp(struct ib_qp *ibqp)
+{
+ return container_of(ibqp, struct nes_qp, ibqp);
+}
+
+
+
+/* nes.c */
+void nes_add_ref(struct ib_qp *);
+void nes_rem_ref(struct ib_qp *);
+struct ib_qp *nes_get_qp(struct ib_device *, int);
+
+
+/* nes_hw.c */
+struct nes_adapter *nes_init_adapter(struct nes_device *, u8);
+void nes_nic_init_timer_defaults(struct nes_device *, u8);
+unsigned int nes_reset_adapter_ne020(struct nes_device *, u8 *);
+int nes_init_serdes(struct nes_device *, u8, u8, u8);
+void nes_init_csr_ne020(struct nes_device *, u8, u8);
+void nes_destroy_adapter(struct nes_adapter *);
+int nes_init_cqp(struct nes_device *);
+int nes_init_phy(struct nes_device *);
+int nes_init_nic_qp(struct nes_device *, struct net_device *);
+void nes_destroy_nic_qp(struct nes_vnic *);
+int nes_napi_isr(struct nes_device *);
+void nes_dpc(unsigned long);
+void nes_process_ceq(struct nes_device *, struct nes_hw_ceq *);
+void nes_process_aeq(struct nes_device *, struct nes_hw_aeq *);
+void nes_process_mac_intr(struct nes_device *, u32);
+void nes_nic_napi_ce_handler(struct nes_device *, struct nes_hw_nic_cq *);
+void nes_nic_ce_handler(struct nes_device *, struct nes_hw_nic_cq *);
+void nes_cqp_ce_handler(struct nes_device *, struct nes_hw_cq *);
+void nes_process_iwarp_aeqe(struct nes_device *, struct nes_hw_aeqe *);
+void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *);
+int nes_destroy_cqp(struct nes_device *);
+int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
+
+/* nes_nic.c */
+void nes_netdev_set_multicast_list(struct net_device *);
+void nes_netdev_exit(struct nes_vnic *);
+struct net_device *nes_netdev_init(struct nes_device *, void __iomem *);
+void nes_netdev_destroy(struct net_device *);
+int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
+
+/* nes_cm.c */
+void *nes_cm_create(struct net_device *);
+int nes_cm_recv(struct sk_buff *, struct net_device *);
+void nes_update_arp(unsigned char *, u32, u32, u16, u16);
+void nes_manage_arp_cache(struct net_device *, unsigned char *, u32, u32);
+void nes_sock_release(struct nes_qp *, unsigned long *);
+struct nes_cm_core *nes_cm_alloc_core(void);
+void flush_wqes(struct nes_device *nesdev, struct nes_qp *, u32, u32);
+int nes_manage_apbvt(struct nes_vnic *, u32, u32, u32);
+int nes_cm_disconn(struct nes_qp *);
+void nes_cm_disconn_worker(void *);
+
+/* nes_verbs.c */
+int nes_hw_modify_qp(struct nes_device *, struct nes_qp *, u32, u32);
+int nes_modify_qp(struct ib_qp *, struct ib_qp_attr *, int, struct ib_udata *);
+struct nes_ib_device *nes_init_ofa_device(struct net_device *);
+void nes_destroy_ofa_device(struct nes_ib_device *);
+int nes_register_ofa_device(struct nes_ib_device *);
+void nes_unregister_ofa_device(struct nes_ib_device *);
+
+/* nes_util.c */
+int nes_read_eeprom_values(struct nes_device *, struct nes_adapter *);
+void nes_write_1G_phy_reg(struct nes_device *, u8, u8, u16);
+void nes_read_1G_phy_reg(struct nes_device *, u8, u8, u16 *);
+void nes_write_10G_phy_reg(struct nes_device *, u16, u8, u16);
+void nes_read_10G_phy_reg(struct nes_device *, u16, u8);
+struct nes_cqp_request *nes_get_cqp_request(struct nes_device *);
+void nes_post_cqp_request(struct nes_device *, struct nes_cqp_request *, int);
+int nes_arp_table(struct nes_device *, u32, u8 *, u32);
+void nes_mh_fix(unsigned long);
+void nes_clc(unsigned long);
+void nes_dump_mem(unsigned int, void *, int);
+u32 nes_crc32(u32, u32, u32, u32, u8 *, u32, u32, u32);
+
+#endif /* __NES_H */
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+
+#define TCPOPT_TIMESTAMP 8
+
+#include <asm/atomic.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <linux/notifier.h>
+#include <linux/net.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/list.h>
+#include <linux/threads.h>
+
+#include <net/neighbour.h>
+#include <net/route.h>
+#include <net/ip_fib.h>
+
+#include "nes.h"
+
+u32 cm_packets_sent;
+u32 cm_packets_bounced;
+u32 cm_packets_dropped;
+u32 cm_packets_retrans;
+u32 cm_packets_created;
+u32 cm_packets_received;
+u32 cm_listens_created;
+u32 cm_listens_destroyed;
+u32 cm_backlog_drops;
+atomic_t cm_loopbacks;
+atomic_t cm_nodes_created;
+atomic_t cm_nodes_destroyed;
+atomic_t cm_accel_dropped_pkts;
+atomic_t cm_resets_recvd;
+
+static inline int mini_cm_accelerated(struct nes_cm_core *, struct nes_cm_node *);
+static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *,
+ struct nes_vnic *, struct nes_cm_info *);
+static int add_ref_cm_node(struct nes_cm_node *);
+static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *);
+static int mini_cm_del_listen(struct nes_cm_core *, struct nes_cm_listener *);
+
+
+/* External CM API Interface */
+/* instance of function pointers for client API */
+/* set address of this instance to cm_core->cm_ops at cm_core alloc */
+static struct nes_cm_ops nes_cm_api = {
+ mini_cm_accelerated,
+ mini_cm_listen,
+ mini_cm_del_listen,
+ mini_cm_connect,
+ mini_cm_close,
+ mini_cm_accept,
+ mini_cm_reject,
+ mini_cm_recv_pkt,
+ mini_cm_dealloc_core,
+ mini_cm_get,
+ mini_cm_set
+};
+
+struct nes_cm_core *g_cm_core;
+
+atomic_t cm_connects;
+atomic_t cm_accepts;
+atomic_t cm_disconnects;
+atomic_t cm_closes;
+atomic_t cm_connecteds;
+atomic_t cm_connect_reqs;
+atomic_t cm_rejects;
+
+
+/**
+ * create_event
+ */
+static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
+ enum nes_cm_event_type type)
+{
+ struct nes_cm_event *event;
+
+ if (!cm_node->cm_id)
+ return NULL;
+
+ /* allocate an empty event */
+ event = kzalloc(sizeof(*event), GFP_ATOMIC);
+
+ if (!event)
+ return NULL;
+
+ event->type = type;
+ event->cm_node = cm_node;
+ event->cm_info.rem_addr = cm_node->rem_addr;
+ event->cm_info.loc_addr = cm_node->loc_addr;
+ event->cm_info.rem_port = cm_node->rem_port;
+ event->cm_info.loc_port = cm_node->loc_port;
+ event->cm_info.cm_id = cm_node->cm_id;
+
+ nes_debug(NES_DBG_CM, "Created event=%p, type=%u, dst_addr=%08x[%x],"
+ " src_addr=%08x[%x]\n",
+ event, type,
+ event->cm_info.loc_addr, event->cm_info.loc_port,
+ event->cm_info.rem_addr, event->cm_info.rem_port);
+
+ nes_cm_post_event(event);
+ return event;
+}
+
+
+/**
+ * send_mpa_request
+ */
+int send_mpa_request(struct nes_cm_node *cm_node)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ skb = get_free_pkt(cm_node);
+ if (!skb) {
+ nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+ return -1;
+ }
+
+ /* send an MPA Request frame */
+ form_cm_frame(skb, cm_node, NULL, 0, &cm_node->mpa_frame,
+ cm_node->mpa_frame_size, SET_ACK);
+
+ ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/**
+ * recv_mpa - process a received TCP pkt, we are expecting an
+ * IETF MPA frame
+ */
+static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 len)
+{
+ struct ietf_mpa_frame *mpa_frame;
+
+ /* assume req frame is in tcp data payload */
+ if (len < sizeof(struct ietf_mpa_frame)) {
+ nes_debug(NES_DBG_CM, "The received ietf buffer was too small (%x)\n", len);
+ return -1;
+ }
+
+ mpa_frame = (struct ietf_mpa_frame *)buffer;
+ cm_node->mpa_frame_size = ntohs(mpa_frame->priv_data_len);
+
+ if (cm_node->mpa_frame_size + sizeof(struct ietf_mpa_frame) != len) {
+ nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
+ " complete (%x + %x != %x)\n",
+ cm_node->mpa_frame_size, (u32)sizeof(struct ietf_mpa_frame), len);
+ return -1;
+ }
+
+ /* copy entire MPA frame to our cm_node's frame */
+ memcpy(cm_node->mpa_frame_buf, buffer + sizeof(struct ietf_mpa_frame),
+ cm_node->mpa_frame_size);
+
+ return 0;
+}
+
+
+/**
+ * handle_exception_pkt - process an exception packet.
+ * We have been in a TSA state, and we have now received SW
+ * TCP/IP traffic should be a FIN request or IP pkt with options
+ */
+static int handle_exception_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb)
+{
+ int ret = 0;
+ struct tcphdr *tcph = tcp_hdr(skb);
+
+ /* first check to see if this a FIN pkt */
+ if (tcph->fin) {
+ /* we need to ACK the FIN request */
+ send_ack(cm_node);
+
+ /* check which side we are (client/server) and set next state accordingly */
+ if (cm_node->tcp_cntxt.client)
+ cm_node->state = NES_CM_STATE_CLOSING;
+ else {
+ /* we are the server side */
+ cm_node->state = NES_CM_STATE_CLOSE_WAIT;
+ /* since this is a self contained CM we don't wait for */
+ /* an APP to close us, just send final FIN immediately */
+ ret = send_fin(cm_node, NULL);
+ cm_node->state = NES_CM_STATE_LAST_ACK;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+
+/**
+ * form_cm_frame - get a free packet and build empty frame Use
+ * node info to build.
+ */
+struct sk_buff *form_cm_frame(struct sk_buff *skb, struct nes_cm_node *cm_node,
+ void *options, u32 optionsize, void *data, u32 datasize, u8 flags)
+{
+ struct tcphdr *tcph;
+ struct iphdr *iph;
+ struct ethhdr *ethh;
+ u8 *buf;
+ u16 packetsize = sizeof(*iph);
+
+ packetsize += sizeof(*tcph);
+ packetsize += optionsize + datasize;
+
+ memset(skb->data, 0x00, ETH_HLEN + sizeof(*iph) + sizeof(*tcph));
+
+ skb->len = 0;
+ buf = skb_put(skb, packetsize + ETH_HLEN);
+
+ ethh = (struct ethhdr *) buf;
+ buf += ETH_HLEN;
+
+ iph = (struct iphdr *)buf;
+ buf += sizeof(*iph);
+ tcph = (struct tcphdr *)buf;
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, ETH_HLEN);
+ skb_set_transport_header(skb, ETH_HLEN+sizeof(*iph));
+ buf += sizeof(*tcph);
+
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->protocol = htons(0x800);
+ skb->data_len = 0;
+ skb->mac_len = ETH_HLEN;
+
+ memcpy(ethh->h_dest, cm_node->rem_mac, ETH_ALEN);
+ memcpy(ethh->h_source, cm_node->loc_mac, ETH_ALEN);
+ ethh->h_proto = htons(0x0800);
+
+ iph->version = IPVERSION;
+ iph->ihl = 5; /* 5 * 4Byte words, IP headr len */
+ iph->tos = 0;
+ iph->tot_len = htons(packetsize);
+ iph->id = htons(++cm_node->tcp_cntxt.loc_id);
+
+ iph->frag_off = htons(0x4000);
+ iph->ttl = 0x40;
+ iph->protocol = 0x06; /* IPPROTO_TCP */
+
+ iph->saddr = htonl(cm_node->loc_addr);
+ iph->daddr = htonl(cm_node->rem_addr);
+
+ tcph->source = htons(cm_node->loc_port);
+ tcph->dest = htons(cm_node->rem_port);
+ tcph->seq = htonl(cm_node->tcp_cntxt.loc_seq_num);
+
+ if (flags & SET_ACK) {
+ cm_node->tcp_cntxt.loc_ack_num = cm_node->tcp_cntxt.rcv_nxt;
+ tcph->ack_seq = htonl(cm_node->tcp_cntxt.loc_ack_num);
+ tcph->ack = 1;
+ } else
+ tcph->ack_seq = 0;
+
+ if (flags & SET_SYN) {
+ cm_node->tcp_cntxt.loc_seq_num++;
+ tcph->syn = 1;
+ } else
+ cm_node->tcp_cntxt.loc_seq_num += datasize; /* data (no headers) */
+
+ if (flags & SET_FIN)
+ tcph->fin = 1;
+
+ if (flags & SET_RST)
+ tcph->rst = 1;
+
+ tcph->doff = (u16)((sizeof(*tcph) + optionsize + 3) >> 2);
+ tcph->window = htons(cm_node->tcp_cntxt.rcv_wnd);
+ tcph->urg_ptr = 0;
+ if (optionsize)
+ memcpy(buf, options, optionsize);
+ buf += optionsize;
+ if (datasize)
+ memcpy(buf, data, datasize);
+
+ skb_shinfo(skb)->nr_frags = 0;
+ cm_packets_created++;
+
+ return skb;
+}
+
+
+/**
+ * print_core - dump a cm core
+ */
+static void print_core(struct nes_cm_core *core)
+{
+ nes_debug(NES_DBG_CM, "---------------------------------------------\n");
+ nes_debug(NES_DBG_CM, "CM Core -- (core = %p )\n", core);
+ if (!core)
+ return;
+ nes_debug(NES_DBG_CM, "---------------------------------------------\n");
+ nes_debug(NES_DBG_CM, "Session ID : %u \n", atomic_read(&core->session_id));
+
+ nes_debug(NES_DBG_CM, "State : %u \n", core->state);
+
+ nes_debug(NES_DBG_CM, "Tx Free cnt : %u \n", skb_queue_len(&core->tx_free_list));
+ nes_debug(NES_DBG_CM, "Listen Nodes : %u \n", atomic_read(&core->listen_node_cnt));
+ nes_debug(NES_DBG_CM, "Active Nodes : %u \n", atomic_read(&core->node_cnt));
+
+ nes_debug(NES_DBG_CM, "core : %p \n", core);
+
+ nes_debug(NES_DBG_CM, "-------------- end core ---------------\n");
+}
+
+
+/**
+ * schedule_nes_timer
+ * note - cm_node needs to be protected before calling this. Encase in:
+ * rem_ref_cm_node(cm_core, cm_node);add_ref_cm_node(cm_node);
+ */
+int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
+ enum nes_timer_type type, int send_retrans,
+ int close_when_complete)
+{
+ unsigned long flags;
+ struct nes_cm_core *cm_core;
+ struct nes_timer_entry *new_send;
+ int ret = 0;
+ u32 was_timer_set;
+
+ new_send = kzalloc(sizeof(*new_send), GFP_ATOMIC);
+ if (!new_send)
+ return -1;
+ if (!cm_node)
+ return -EINVAL;
+
+ /* new_send->timetosend = currenttime */
+ new_send->retrycount = NES_DEFAULT_RETRYS;
+ new_send->retranscount = NES_DEFAULT_RETRANS;
+ new_send->skb = skb;
+ new_send->timetosend = jiffies;
+ new_send->type = type;
+ new_send->netdev = cm_node->netdev;
+ new_send->send_retrans = send_retrans;
+ new_send->close_when_complete = close_when_complete;
+
+ if (type == NES_TIMER_TYPE_CLOSE) {
+ new_send->timetosend += (HZ/2); /* TODO: decide on the correct value here */
+ spin_lock_irqsave(&cm_node->recv_list_lock, flags);
+ list_add_tail(&new_send->list, &cm_node->recv_list);
+ spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
+ }
+
+ if (type == NES_TIMER_TYPE_SEND) {
+ new_send->seq_num = htonl(tcp_hdr(skb)->seq);
+ atomic_inc(&new_send->skb->users);
+
+ ret = nes_nic_cm_xmit(new_send->skb, cm_node->netdev);
+ if (ret != NETDEV_TX_OK) {
+ nes_debug(NES_DBG_CM, "Error sending packet %p (jiffies = %lu)\n",
+ new_send, jiffies);
+ atomic_dec(&new_send->skb->users);
+ new_send->timetosend = jiffies;
+ } else {
+ cm_packets_sent++;
+ if (!send_retrans) {
+ if (close_when_complete)
+ rem_ref_cm_node(cm_node->cm_core, cm_node);
+ dev_kfree_skb_any(new_send->skb);
+ kfree(new_send);
+ return ret;
+ }
+ new_send->timetosend = jiffies + NES_RETRY_TIMEOUT;
+ }
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ list_add_tail(&new_send->list, &cm_node->retrans_list);
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ }
+ if (type == NES_TIMER_TYPE_RECV) {
+ new_send->seq_num = htonl(tcp_hdr(skb)->seq);
+ new_send->timetosend = jiffies;
+ spin_lock_irqsave(&cm_node->recv_list_lock, flags);
+ list_add_tail(&new_send->list, &cm_node->recv_list);
+ spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
+ }
+ cm_core = cm_node->cm_core;
+
+ was_timer_set = timer_pending(&cm_core->tcp_timer);
+
+ if (!was_timer_set) {
+ cm_core->tcp_timer.expires = new_send->timetosend;
+ add_timer(&cm_core->tcp_timer);
+ }
+
+ return ret;
+}
+
+
+/**
+ * nes_cm_timer_tick
+ */
+void nes_cm_timer_tick(unsigned long pass)
+{
+ unsigned long flags, qplockflags;
+ unsigned long nexttimeout = jiffies + NES_LONG_TIME;
+ struct iw_cm_id *cm_id;
+ struct nes_cm_node *cm_node;
+ struct nes_timer_entry *send_entry, *recv_entry;
+ struct list_head *list_core, *list_core_temp;
+ struct list_head *list_node, *list_node_temp;
+ struct nes_cm_core *cm_core = g_cm_core;
+ struct nes_qp *nesqp;
+ struct sk_buff *skb;
+ u32 settimer = 0;
+ int ret = NETDEV_TX_OK;
+ int node_done;
+
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+
+ list_for_each_safe(list_node, list_core_temp, &cm_core->connected_nodes) {
+ cm_node = container_of(list_node, struct nes_cm_node, list);
+ add_ref_cm_node(cm_node);
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+ spin_lock_irqsave(&cm_node->recv_list_lock, flags);
+ list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) {
+ recv_entry = container_of(list_core, struct nes_timer_entry, list);
+ if ((time_after(recv_entry->timetosend, jiffies)) &&
+ (recv_entry->type == NES_TIMER_TYPE_CLOSE)) {
+ if (nexttimeout > recv_entry->timetosend || !settimer) {
+ nexttimeout = recv_entry->timetosend;
+ settimer = 1;
+ }
+ continue;
+ }
+ list_del(&recv_entry->list);
+ cm_id = cm_node->cm_id;
+ spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
+ if (recv_entry->type == NES_TIMER_TYPE_CLOSE) {
+ nesqp = (struct nes_qp *)recv_entry->skb;
+ spin_lock_irqsave(&nesqp->lock, qplockflags);
+ if (nesqp->cm_id) {
+ nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, refcount = %d: "
+ "****** HIT A NES_TIMER_TYPE_CLOSE"
+ " with something to do!!! ******\n",
+ nesqp->hwqp.qp_id, cm_id,
+ atomic_read(&nesqp->refcount));
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
+ nesqp->ibqp_state = IB_QPS_ERR;
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_cm_disconn(nesqp);
+ } else {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, refcount = %d:"
+ " ****** HIT A NES_TIMER_TYPE_CLOSE"
+ " with nothing to do!!! ******\n",
+ nesqp->hwqp.qp_id, cm_id,
+ atomic_read(&nesqp->refcount));
+ nes_rem_ref(&nesqp->ibqp);
+ }
+ if (cm_id)
+ cm_id->rem_ref(cm_id);
+ }
+ kfree(recv_entry);
+ spin_lock_irqsave(&cm_node->recv_list_lock, flags);
+ }
+ spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
+
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ node_done = 0;
+ list_for_each_safe(list_core, list_node_temp, &cm_node->retrans_list) {
+ if (node_done) {
+ break;
+ }
+ send_entry = container_of(list_core, struct nes_timer_entry, list);
+ if (time_after(send_entry->timetosend, jiffies)) {
+ if (cm_node->state != NES_CM_STATE_TSA) {
+ if ((nexttimeout > send_entry->timetosend) || !settimer) {
+ nexttimeout = send_entry->timetosend;
+ settimer = 1;
+ }
+ node_done = 1;
+ continue;
+ } else {
+ list_del(&send_entry->list);
+ skb = send_entry->skb;
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ dev_kfree_skb_any(skb);
+ kfree(send_entry);
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ }
+ }
+ if (send_entry->type == NES_TIMER_NODE_CLEANUP) {
+ list_del(&send_entry->list);
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ kfree(send_entry);
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ }
+ if ((send_entry->seq_num < cm_node->tcp_cntxt.rem_ack_num) ||
+ (cm_node->state == NES_CM_STATE_TSA) ||
+ (cm_node->state == NES_CM_STATE_CLOSED)) {
+ skb = send_entry->skb;
+ list_del(&send_entry->list);
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ kfree(send_entry);
+ dev_kfree_skb_any(skb);
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ }
+
+ if (!send_entry->retranscount || !send_entry->retrycount) {
+ cm_packets_dropped++;
+ skb = send_entry->skb;
+ list_del(&send_entry->list);
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ dev_kfree_skb_any(skb);
+ kfree(send_entry);
+ if (cm_node->state == NES_CM_STATE_SYN_RCVD) {
+ /* this node never even generated an indication up to the cm */
+ rem_ref_cm_node(cm_core, cm_node);
+ } else {
+ cm_node->state = NES_CM_STATE_CLOSED;
+ create_event(cm_node, NES_CM_EVENT_ABORTED);
+ }
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ }
+ /* this seems like the correct place, but leave send entry unprotected */
+ // spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ atomic_inc(&send_entry->skb->users);
+ cm_packets_retrans++;
+ nes_debug(NES_DBG_CM, "Retransmitting send_entry %p for node %p,"
+ " jiffies = %lu, time to send = %lu, retranscount = %u, "
+ "send_entry->seq_num = 0x%08X, cm_node->tcp_cntxt.rem_ack_num = 0x%08X\n",
+ send_entry, cm_node, jiffies, send_entry->timetosend, send_entry->retranscount,
+ send_entry->seq_num, cm_node->tcp_cntxt.rem_ack_num);
+
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ ret = nes_nic_cm_xmit(send_entry->skb, cm_node->netdev);
+ if (ret != NETDEV_TX_OK) {
+ cm_packets_bounced++;
+ atomic_dec(&send_entry->skb->users);
+ send_entry->retrycount--;
+ nexttimeout = jiffies + NES_SHORT_TIME;
+ settimer = 1;
+ node_done = 1;
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ } else {
+ cm_packets_sent++;
+ }
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ list_del(&send_entry->list);
+ nes_debug(NES_DBG_CM, "Packet Sent: retrans count = %u, retry count = %u.\n",
+ send_entry->retranscount, send_entry->retrycount);
+ if (send_entry->send_retrans) {
+ send_entry->retranscount--;
+ send_entry->timetosend = jiffies + NES_RETRY_TIMEOUT;
+ if (nexttimeout > send_entry->timetosend || !settimer) {
+ nexttimeout = send_entry->timetosend;
+ settimer = 1;
+ }
+ list_add(&send_entry->list, &cm_node->retrans_list);
+ continue;
+ } else {
+ int close_when_complete;
+ skb = send_entry->skb;
+ close_when_complete = send_entry->close_when_complete;
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ if (close_when_complete) {
+ BUG_ON(atomic_read(&cm_node->ref_count) == 1);
+ rem_ref_cm_node(cm_core, cm_node);
+ }
+ dev_kfree_skb_any(skb);
+ kfree(send_entry);
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ }
+ }
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+
+ rem_ref_cm_node(cm_core, cm_node);
+
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+ if (ret != NETDEV_TX_OK)
+ break;
+ }
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+
+ if (settimer) {
+ if (!timer_pending(&cm_core->tcp_timer)) {
+ cm_core->tcp_timer.expires = nexttimeout;
+ add_timer(&cm_core->tcp_timer);
+ }
+ }
+}
+
+
+/**
+ * send_syn
+ */
+int send_syn(struct nes_cm_node *cm_node, u32 sendack)
+{
+ int ret;
+ int flags = SET_SYN;
+ struct sk_buff *skb;
+ char optionsbuffer[sizeof(struct option_mss) +
+ sizeof(struct option_windowscale) +
+ sizeof(struct option_base) + 1];
+
+ int optionssize = 0;
+ /* Sending MSS option */
+ union all_known_options *options;
+
+ if (!cm_node)
+ return -EINVAL;
+
+ options = (union all_known_options *)&optionsbuffer[optionssize];
+ options->as_mss.optionnum = OPTION_NUMBER_MSS;
+ options->as_mss.length = sizeof(struct option_mss);
+ options->as_mss.mss = htons(cm_node->tcp_cntxt.mss);
+ optionssize += sizeof(struct option_mss);
+
+ options = (union all_known_options *)&optionsbuffer[optionssize];
+ options->as_windowscale.optionnum = OPTION_NUMBER_WINDOW_SCALE;
+ options->as_windowscale.length = sizeof(struct option_windowscale);
+ options->as_windowscale.shiftcount = cm_node->tcp_cntxt.rcv_wscale;
+ optionssize += sizeof(struct option_windowscale);
+
+ if (sendack && !(NES_DRV_OPT_SUPRESS_OPTION_BC & nes_drv_opt)
+ ) {
+ options = (union all_known_options *)&optionsbuffer[optionssize];
+ options->as_base.optionnum = OPTION_NUMBER_WRITE0;
+ options->as_base.length = sizeof(struct option_base);
+ optionssize += sizeof(struct option_base);
+ /* we need the size to be a multiple of 4 */
+ options = (union all_known_options *)&optionsbuffer[optionssize];
+ options->as_end = 1;
+ optionssize += 1;
+ options = (union all_known_options *)&optionsbuffer[optionssize];
+ options->as_end = 1;
+ optionssize += 1;
+ }
+
+ options = (union all_known_options *)&optionsbuffer[optionssize];
+ options->as_end = OPTION_NUMBER_END;
+ optionssize += 1;
+
+ skb = get_free_pkt(cm_node);
+ if (!skb) {
+ nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+ return -1;
+ }
+
+ if (sendack)
+ flags |= SET_ACK;
+
+ form_cm_frame(skb, cm_node, optionsbuffer, optionssize, NULL, 0, flags);
+ ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
+
+ return ret;
+}
+
+
+/**
+ * send_reset
+ */
+int send_reset(struct nes_cm_node *cm_node)
+{
+ int ret;
+ struct sk_buff *skb = get_free_pkt(cm_node);
+ int flags = SET_RST | SET_ACK;
+
+ if (!skb) {
+ nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+ return -1;
+ }
+
+ add_ref_cm_node(cm_node);
+ form_cm_frame(skb, cm_node, NULL, 0, NULL, 0, flags);
+ ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 0, 1);
+
+ return ret;
+}
+
+
+/**
+ * send_ack
+ */
+int send_ack(struct nes_cm_node *cm_node)
+{
+ int ret;
+ struct sk_buff *skb = get_free_pkt(cm_node);
+
+ if (!skb) {
+ nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+ return -1;
+ }
+
+ form_cm_frame(skb, cm_node, NULL, 0, NULL, 0, SET_ACK);
+ ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 0, 0);
+
+ return ret;
+}
+
+
+/**
+ * send_fin
+ */
+int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb)
+{
+ int ret;
+
+ /* if we didn't get a frame get one */
+ if (!skb)
+ skb = get_free_pkt(cm_node);
+
+ if (!skb) {
+ nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+ return -1;
+ }
+
+ form_cm_frame(skb, cm_node, NULL, 0, NULL, 0, SET_ACK | SET_FIN);
+ ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
+
+ return ret;
+}
+
+
+/**
+ * get_free_pkt
+ */
+struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node)
+{
+ struct sk_buff *skb, *new_skb;
+
+ /* check to see if we need to repopulate the free tx pkt queue */
+ if (skb_queue_len(&cm_node->cm_core->tx_free_list) < NES_CM_FREE_PKT_LO_WATERMARK) {
+ while (skb_queue_len(&cm_node->cm_core->tx_free_list) <
+ cm_node->cm_core->free_tx_pkt_max) {
+ /* replace the frame we took, we won't get it back */
+ new_skb = dev_alloc_skb(cm_node->cm_core->mtu);
+ BUG_ON(!new_skb);
+ /* add a replacement frame to the free tx list head */
+ skb_queue_head(&cm_node->cm_core->tx_free_list, new_skb);
+ }
+ }
+
+ skb = skb_dequeue(&cm_node->cm_core->tx_free_list);
+
+ return skb;
+}
+
+
+/**
+ * make_hashkey - generate hash key from node tuple
+ */
+static inline int make_hashkey(u16 loc_port, nes_addr_t loc_addr, u16 rem_port,
+ nes_addr_t rem_addr)
+{
+ u32 hashkey = 0;
+
+ hashkey = loc_addr + rem_addr + loc_port + rem_port;
+ hashkey = (hashkey % NES_CM_HASHTABLE_SIZE);
+
+ return hashkey;
+}
+
+
+/**
+ * find_node - find a cm node that matches the reference cm node
+ */
+static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
+ u16 rem_port, nes_addr_t rem_addr, u16 loc_port, nes_addr_t loc_addr)
+{
+ unsigned long flags;
+ u32 hashkey;
+ struct list_head *list_pos;
+ struct list_head *hte;
+ struct nes_cm_node *cm_node;
+
+ /* make a hash index key for this packet */
+ hashkey = make_hashkey(loc_port, loc_addr, rem_port, rem_addr);
+
+ /* get a handle on the hte */
+ hte = &cm_core->connected_nodes;
+
+ nes_debug(NES_DBG_CM, "Searching for an owner node:%x:%x from core %p->%p\n",
+ loc_addr, loc_port, cm_core, hte);
+
+ /* walk list and find cm_node associated with this session ID */
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+ list_for_each(list_pos, hte) {
+ cm_node = container_of(list_pos, struct nes_cm_node, list);
+ /* compare quad, return node handle if a match */
+ nes_debug(NES_DBG_CM, "finding node %x:%x =? %x:%x ^ %x:%x =? %x:%x\n",
+ cm_node->loc_addr, cm_node->loc_port,
+ loc_addr, loc_port,
+ cm_node->rem_addr, cm_node->rem_port,
+ rem_addr, rem_port);
+ if ((cm_node->loc_addr == loc_addr) && (cm_node->loc_port == loc_port) &&
+ (cm_node->rem_addr == rem_addr) && (cm_node->rem_port == rem_port)) {
+ add_ref_cm_node(cm_node);
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+ return cm_node;
+ }
+ }
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+
+ /* no owner node */
+ return NULL;
+}
+
+
+/**
+ * find_listener - find a cm node listening on this addr-port pair
+ */
+static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
+ nes_addr_t dst_addr, u16 dst_port, enum nes_cm_listener_state listener_state)
+{
+ unsigned long flags;
+ struct list_head *listen_list;
+ struct nes_cm_listener *listen_node;
+
+ /* walk list and find cm_node associated with this session ID */
+ spin_lock_irqsave(&cm_core->listen_list_lock, flags);
+ list_for_each(listen_list, &cm_core->listen_list.list) {
+ listen_node = container_of(listen_list, struct nes_cm_listener, list);
+ /* compare node pair, return node handle if a match */
+ if (((listen_node->loc_addr == dst_addr) ||
+ listen_node->loc_addr == 0x00000000) &&
+ (listen_node->loc_port == dst_port) &&
+ (listener_state & listen_node->listener_state)) {
+ atomic_inc(&listen_node->ref_count);
+ spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
+ return listen_node;
+ }
+ }
+ spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
+
+ nes_debug(NES_DBG_CM, "Unable to find listener- %x:%x\n",
+ dst_addr, dst_port);
+
+ /* no listener */
+ return NULL;
+}
+
+
+/**
+ * add_hte_node - add a cm node to the hash table
+ */
+static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
+{
+ unsigned long flags;
+ u32 hashkey;
+ struct list_head *hte;
+
+ if (!cm_node || !cm_core)
+ return -EINVAL;
+
+ nes_debug(NES_DBG_CM, "Adding Node to Active Connection HT\n");
+
+ /* first, make an index into our hash table */
+ hashkey = make_hashkey(cm_node->loc_port, cm_node->loc_addr,
+ cm_node->rem_port, cm_node->rem_addr);
+ cm_node->hashkey = hashkey;
+
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+
+ /* get a handle on the hash table element (list head for this slot) */
+ hte = &cm_core->connected_nodes;
+ list_add_tail(&cm_node->list, hte);
+ atomic_inc(&cm_core->ht_node_cnt);
+
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+
+ return 0;
+}
+
+
+/**
+ * mini_cm_dec_refcnt_listen
+ */
+static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
+ struct nes_cm_listener *listener, int free_hanging_nodes)
+{
+ int ret = 1;
+ unsigned long flags;
+ spin_lock_irqsave(&cm_core->listen_list_lock, flags);
+ if (!atomic_dec_return(&listener->ref_count)) {
+ list_del(&listener->list);
+
+ /* decrement our listen node count */
+ atomic_dec(&cm_core->listen_node_cnt);
+
+ spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
+
+ if (listener->nesvnic) {
+ nes_manage_apbvt(listener->nesvnic, listener->loc_port,
+ PCI_FUNC(listener->nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
+ }
+
+ nes_debug(NES_DBG_CM, "destroying listener (%p)\n", listener);
+
+ kfree(listener);
+ ret = 0;
+ cm_listens_destroyed++;
+ } else {
+ spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
+ }
+ if (listener) {
+ if (atomic_read(&listener->pend_accepts_cnt) > 0)
+ nes_debug(NES_DBG_CM, "destroying listener (%p)"
+ " with non-zero pending accepts=%u\n",
+ listener, atomic_read(&listener->pend_accepts_cnt));
+ }
+
+ return ret;
+}
+
+
+/**
+ * mini_cm_del_listen
+ */
+static int mini_cm_del_listen(struct nes_cm_core *cm_core,
+ struct nes_cm_listener *listener)
+{
+ listener->listener_state = NES_CM_LISTENER_PASSIVE_STATE;
+ listener->cm_id = NULL; /* going to be destroyed pretty soon */
+ return mini_cm_dec_refcnt_listen(cm_core, listener, 1);
+}
+
+
+/**
+ * mini_cm_accelerated
+ */
+static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
+ struct nes_cm_node *cm_node)
+{
+ u32 was_timer_set;
+ cm_node->accelerated = 1;
+
+ if (cm_node->accept_pend) {
+ BUG_ON(!cm_node->listener);
+ atomic_dec(&cm_node->listener->pend_accepts_cnt);
+ BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
+ }
+
+ was_timer_set = timer_pending(&cm_core->tcp_timer);
+ if (!was_timer_set) {
+ cm_core->tcp_timer.expires = jiffies + NES_SHORT_TIME;
+ add_timer(&cm_core->tcp_timer);
+ }
+
+ return 0;
+}
+
+
+/**
+ * nes_addr_send_arp
+ */
+static void nes_addr_send_arp(u32 dst_ip)
+{
+ struct rtable *rt;
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof fl);
+ fl.nl_u.ip4_u.daddr = htonl(dst_ip);
+ if (ip_route_output_key(&init_net, &rt, &fl)) {
+ printk("%s: ip_route_output_key failed for 0x%08X\n",
+ __FUNCTION__, dst_ip);
+ return;
+ }
+
+ neigh_event_send(rt->u.dst.neighbour, NULL);
+ ip_rt_put(rt);
+}
+
+
+/**
+ * make_cm_node - create a new instance of a cm node
+ */
+static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
+ struct nes_vnic *nesvnic, struct nes_cm_info *cm_info,
+ struct nes_cm_listener *listener)
+{
+ struct nes_cm_node *cm_node;
+ struct timespec ts;
+ int arpindex = 0;
+ struct nes_device *nesdev;
+ struct nes_adapter *nesadapter;
+
+ /* create an hte and cm_node for this instance */
+ cm_node = kzalloc(sizeof(*cm_node), GFP_ATOMIC);
+ if (!cm_node)
+ return NULL;
+
+ /* set our node specific transport info */
+ cm_node->loc_addr = cm_info->loc_addr;
+ cm_node->rem_addr = cm_info->rem_addr;
+ cm_node->loc_port = cm_info->loc_port;
+ cm_node->rem_port = cm_info->rem_port;
+ cm_node->send_write0 = send_first;
+ nes_debug(NES_DBG_CM, "Make node addresses : loc = %x:%x, rem = %x:%x\n",
+ cm_node->loc_addr, cm_node->loc_port, cm_node->rem_addr, cm_node->rem_port);
+ cm_node->listener = listener;
+ cm_node->netdev = nesvnic->netdev;
+ cm_node->cm_id = cm_info->cm_id;
+ memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN);
+
+ nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n",
+ cm_node->listener, cm_node->cm_id);
+
+ INIT_LIST_HEAD(&cm_node->retrans_list);
+ spin_lock_init(&cm_node->retrans_list_lock);
+ INIT_LIST_HEAD(&cm_node->recv_list);
+ spin_lock_init(&cm_node->recv_list_lock);
+
+ cm_node->loopbackpartner = NULL;
+ atomic_set(&cm_node->ref_count, 1);
+ /* associate our parent CM core */
+ cm_node->cm_core = cm_core;
+ cm_node->tcp_cntxt.loc_id = NES_CM_DEF_LOCAL_ID;
+ cm_node->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
+ cm_node->tcp_cntxt.rcv_wnd = NES_CM_DEFAULT_RCV_WND_SCALED >>
+ NES_CM_DEFAULT_RCV_WND_SCALE;
+ ts = current_kernel_time();
+ cm_node->tcp_cntxt.loc_seq_num = htonl(ts.tv_nsec);
+ cm_node->tcp_cntxt.mss = nesvnic->max_frame_size - sizeof(struct iphdr) -
+ sizeof(struct tcphdr) - ETH_HLEN;
+ cm_node->tcp_cntxt.rcv_nxt = 0;
+ /* get a unique session ID , add thread_id to an upcounter to handle race */
+ atomic_inc(&cm_core->node_cnt);
+ atomic_inc(&cm_core->session_id);
+ cm_node->session_id = (u32)(atomic_read(&cm_core->session_id) + current->tgid);
+ cm_node->conn_type = cm_info->conn_type;
+ cm_node->apbvt_set = 0;
+ cm_node->accept_pend = 0;
+
+ cm_node->nesvnic = nesvnic;
+ /* get some device handles, for arp lookup */
+ nesdev = nesvnic->nesdev;
+ nesadapter = nesdev->nesadapter;
+
+ cm_node->loopbackpartner = NULL;
+ /* get the mac addr for the remote node */
+ arpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
+ if (arpindex < 0) {
+ kfree(cm_node);
+ nes_addr_send_arp(cm_info->rem_addr);
+ return NULL;
+ }
+
+ /* copy the mac addr to node context */
+ memcpy(cm_node->rem_mac, nesadapter->arp_table[arpindex].mac_addr, ETH_ALEN);
+ nes_debug(NES_DBG_CM, "Remote mac addr from arp table:%02x,"
+ " %02x, %02x, %02x, %02x, %02x\n",
+ cm_node->rem_mac[0], cm_node->rem_mac[1],
+ cm_node->rem_mac[2], cm_node->rem_mac[3],
+ cm_node->rem_mac[4], cm_node->rem_mac[5]);
+
+ add_hte_node(cm_core, cm_node);
+ atomic_inc(&cm_nodes_created);
+
+ return cm_node;
+}
+
+
+/**
+ * add_ref_cm_node - destroy an instance of a cm node
+ */
+static int add_ref_cm_node(struct nes_cm_node *cm_node)
+{
+ atomic_inc(&cm_node->ref_count);
+ return 0;
+}
+
+
+/**
+ * rem_ref_cm_node - destroy an instance of a cm node
+ */
+static int rem_ref_cm_node(struct nes_cm_core *cm_core,
+ struct nes_cm_node *cm_node)
+{
+ unsigned long flags, qplockflags;
+ struct nes_timer_entry *send_entry;
+ struct nes_timer_entry *recv_entry;
+ struct iw_cm_id *cm_id;
+ struct list_head *list_core, *list_node_temp;
+ struct nes_qp *nesqp;
+
+ if (!cm_node)
+ return -EINVAL;
+
+ spin_lock_irqsave(&cm_node->cm_core->ht_lock, flags);
+ if (atomic_dec_return(&cm_node->ref_count)) {
+ spin_unlock_irqrestore(&cm_node->cm_core->ht_lock, flags);
+ return 0;
+ }
+ list_del(&cm_node->list);
+ atomic_dec(&cm_core->ht_node_cnt);
+ spin_unlock_irqrestore(&cm_node->cm_core->ht_lock, flags);
+
+ /* if the node is destroyed before connection was accelerated */
+ if (!cm_node->accelerated && cm_node->accept_pend) {
+ BUG_ON(!cm_node->listener);
+ atomic_dec(&cm_node->listener->pend_accepts_cnt);
+ BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
+ }
+
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ list_for_each_safe(list_core, list_node_temp, &cm_node->retrans_list) {
+ send_entry = container_of(list_core, struct nes_timer_entry, list);
+ list_del(&send_entry->list);
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+ dev_kfree_skb_any(send_entry->skb);
+ kfree(send_entry);
+ spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+ continue;
+ }
+ spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+
+ spin_lock_irqsave(&cm_node->recv_list_lock, flags);
+ list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) {
+ recv_entry = container_of(list_core, struct nes_timer_entry, list);
+ list_del(&recv_entry->list);
+ cm_id = cm_node->cm_id;
+ spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
+ if (recv_entry->type == NES_TIMER_TYPE_CLOSE) {
+ nesqp = (struct nes_qp *)recv_entry->skb;
+ spin_lock_irqsave(&nesqp->lock, qplockflags);
+ if (nesqp->cm_id) {
+ nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: ****** HIT A NES_TIMER_TYPE_CLOSE"
+ " with something to do!!! ******\n",
+ nesqp->hwqp.qp_id, cm_id);
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
+ nesqp->ibqp_state = IB_QPS_ERR;
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_cm_disconn(nesqp);
+ } else {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: ****** HIT A NES_TIMER_TYPE_CLOSE"
+ " with nothing to do!!! ******\n",
+ nesqp->hwqp.qp_id, cm_id);
+ nes_rem_ref(&nesqp->ibqp);
+ }
+ cm_id->rem_ref(cm_id);
+ } else if (recv_entry->type == NES_TIMER_TYPE_RECV) {
+ dev_kfree_skb_any(recv_entry->skb);
+ }
+ kfree(recv_entry);
+ spin_lock_irqsave(&cm_node->recv_list_lock, flags);
+ }
+ spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
+
+ if (cm_node->listener) {
+ mini_cm_dec_refcnt_listen(cm_core, cm_node->listener, 0);
+ } else {
+ if (cm_node->apbvt_set && cm_node->nesvnic) {
+ nes_manage_apbvt(cm_node->nesvnic, cm_node->loc_port,
+ PCI_FUNC(cm_node->nesvnic->nesdev->pcidev->devfn),
+ NES_MANAGE_APBVT_DEL);
+ }
+ }
+
+ kfree(cm_node);
+ atomic_dec(&cm_core->node_cnt);
+ atomic_inc(&cm_nodes_destroyed);
+
+ return 0;
+}
+
+
+/**
+ * process_options
+ */
+static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 optionsize, u32 syn_packet)
+{
+ u32 tmp;
+ u32 offset = 0;
+ union all_known_options *all_options;
+ char got_mss_option = 0;
+
+ while (offset < optionsize) {
+ all_options = (union all_known_options *)(optionsloc + offset);
+ switch (all_options->as_base.optionnum) {
+ case OPTION_NUMBER_END:
+ offset = optionsize;
+ break;
+ case OPTION_NUMBER_NONE:
+ offset += 1;
+ continue;
+ case OPTION_NUMBER_MSS:
+ nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d Size: %d\n",
+ __FUNCTION__,
+ all_options->as_mss.length, offset, optionsize);
+ got_mss_option = 1;
+ if (all_options->as_mss.length != 4) {
+ return 1;
+ } else {
+ tmp = ntohs(all_options->as_mss.mss);
+ if (tmp > 0 && tmp < cm_node->tcp_cntxt.mss)
+ cm_node->tcp_cntxt.mss = tmp;
+ }
+ break;
+ case OPTION_NUMBER_WINDOW_SCALE:
+ cm_node->tcp_cntxt.snd_wscale = all_options->as_windowscale.shiftcount;
+ break;
+ case OPTION_NUMBER_WRITE0:
+ cm_node->send_write0 = 1;
+ break;
+ default:
+ nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n",
+ all_options->as_base.optionnum);
+ break;
+ }
+ offset += all_options->as_base.length;
+ }
+ if ((!got_mss_option) && (syn_packet))
+ cm_node->tcp_cntxt.mss = NES_CM_DEFAULT_MSS;
+ return 0;
+}
+
+
+/**
+ * process_packet
+ */
+int process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
+ struct nes_cm_core *cm_core)
+{
+ int optionsize;
+ int datasize;
+ int ret = 0;
+ struct tcphdr *tcph = tcp_hdr(skb);
+ u32 inc_sequence;
+ if (cm_node->state == NES_CM_STATE_SYN_SENT && tcph->syn) {
+ inc_sequence = ntohl(tcph->seq);
+ cm_node->tcp_cntxt.rcv_nxt = inc_sequence;
+ }
+
+ if ((!tcph) || (cm_node->state == NES_CM_STATE_TSA)) {
+ BUG_ON(!tcph);
+ atomic_inc(&cm_accel_dropped_pkts);
+ return -1;
+ }
+
+ if (tcph->rst) {
+ atomic_inc(&cm_resets_recvd);
+ nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u. refcnt=%d\n",
+ cm_node, cm_node->state, atomic_read(&cm_node->ref_count));
+ switch (cm_node->state) {
+ case NES_CM_STATE_LISTENING:
+ rem_ref_cm_node(cm_core, cm_node);
+ break;
+ case NES_CM_STATE_TSA:
+ case NES_CM_STATE_CLOSED:
+ break;
+ case NES_CM_STATE_SYN_RCVD:
+ nes_debug(NES_DBG_CM, "Received a reset for local 0x%08X:%04X,"
+ " remote 0x%08X:%04X, node state = %u\n",
+ cm_node->loc_addr, cm_node->loc_port,
+ cm_node->rem_addr, cm_node->rem_port,
+ cm_node->state);
+ rem_ref_cm_node(cm_core, cm_node);
+ break;
+ case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
+ case NES_CM_STATE_ESTABLISHED:
+ case NES_CM_STATE_MPAREQ_SENT:
+ default:
+ nes_debug(NES_DBG_CM, "Received a reset for local 0x%08X:%04X,"
+ " remote 0x%08X:%04X, node state = %u refcnt=%d\n",
+ cm_node->loc_addr, cm_node->loc_port,
+ cm_node->rem_addr, cm_node->rem_port,
+ cm_node->state, atomic_read(&cm_node->ref_count));
+ // create event
+ cm_node->state = NES_CM_STATE_CLOSED;
+
+ create_event(cm_node, NES_CM_EVENT_ABORTED);
+ break;
+
+ }
+ return -1;
+ }
+
+ optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
+
+ skb_pull(skb, ip_hdr(skb)->ihl << 2);
+ skb_pull(skb, tcph->doff << 2);
+
+ datasize = skb->len;
+ inc_sequence = ntohl(tcph->seq);
+ nes_debug(NES_DBG_CM, "datasize = %u, sequence = 0x%08X, ack_seq = 0x%08X,"
+ " rcv_nxt = 0x%08X Flags: %s %s.\n",
+ datasize, inc_sequence, ntohl(tcph->ack_seq),
+ cm_node->tcp_cntxt.rcv_nxt, (tcph->syn ? "SYN":""),
+ (tcph->ack ? "ACK":""));
+
+ if (!tcph->syn && (inc_sequence != cm_node->tcp_cntxt.rcv_nxt)
+ ) {
+ nes_debug(NES_DBG_CM, "dropping packet, datasize = %u, sequence = 0x%08X,"
+ " ack_seq = 0x%08X, rcv_nxt = 0x%08X Flags: %s.\n",
+ datasize, inc_sequence, ntohl(tcph->ack_seq),
+ cm_node->tcp_cntxt.rcv_nxt, (tcph->ack ? "ACK":""));
+ if (cm_node->state == NES_CM_STATE_LISTENING) {
+ rem_ref_cm_node(cm_core, cm_node);
+ }
+ return -1;
+ }
+
+ cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
+
+
+ if (optionsize) {
+ u8 *optionsloc = (u8 *)&tcph[1];
+ if (process_options(cm_node, optionsloc, optionsize, (u32)tcph->syn)) {
+ nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n", __FUNCTION__, cm_node);
+ send_reset(cm_node);
+ if (cm_node->state != NES_CM_STATE_SYN_SENT)
+ rem_ref_cm_node(cm_core, cm_node);
+ return 0;
+ }
+ } else if (tcph->syn)
+ cm_node->tcp_cntxt.mss = NES_CM_DEFAULT_MSS;
+
+ cm_node->tcp_cntxt.snd_wnd = ntohs(tcph->window) <<
+ cm_node->tcp_cntxt.snd_wscale;
+
+ if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd) {
+ cm_node->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.snd_wnd;
+ }
+
+ if (tcph->ack) {
+ cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
+ switch (cm_node->state) {
+ case NES_CM_STATE_SYN_RCVD:
+ case NES_CM_STATE_SYN_SENT:
+ /* read and stash current sequence number */
+ if (cm_node->tcp_cntxt.rem_ack_num != cm_node->tcp_cntxt.loc_seq_num) {
+ nes_debug(NES_DBG_CM, "ERROR - cm_node->tcp_cntxt.rem_ack_num !="
+ " cm_node->tcp_cntxt.loc_seq_num\n");
+ send_reset(cm_node);
+ return 0;
+ }
+ if (cm_node->state == NES_CM_STATE_SYN_SENT)
+ cm_node->state = NES_CM_STATE_ONE_SIDE_ESTABLISHED;
+ else {
+ cm_node->state = NES_CM_STATE_ESTABLISHED;
+ }
+ break;
+ case NES_CM_STATE_LAST_ACK:
+ cm_node->state = NES_CM_STATE_CLOSED;
+ break;
+ case NES_CM_STATE_FIN_WAIT1:
+ cm_node->state = NES_CM_STATE_FIN_WAIT2;
+ break;
+ case NES_CM_STATE_CLOSING:
+ cm_node->state = NES_CM_STATE_TIME_WAIT;
+ /* need to schedule this to happen in 2MSL timeouts */
+ cm_node->state = NES_CM_STATE_CLOSED;
+ break;
+ case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
+ case NES_CM_STATE_ESTABLISHED:
+ case NES_CM_STATE_MPAREQ_SENT:
+ case NES_CM_STATE_CLOSE_WAIT:
+ case NES_CM_STATE_TIME_WAIT:
+ case NES_CM_STATE_CLOSED:
+ break;
+ case NES_CM_STATE_LISTENING:
+ nes_debug(NES_DBG_CM, "Received an ACK on a listening port (SYN %d)\n", tcph->syn);
+ cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
+ send_reset(cm_node);
+ /* send_reset bumps refcount, this should have been a new node */
+ rem_ref_cm_node(cm_core, cm_node);
+ return -1;
+ break;
+ case NES_CM_STATE_TSA:
+ nes_debug(NES_DBG_CM, "Received a packet with the ack bit set while in TSA state\n");
+ break;
+ case NES_CM_STATE_UNKNOWN:
+ case NES_CM_STATE_INITED:
+ case NES_CM_STATE_ACCEPTING:
+ case NES_CM_STATE_FIN_WAIT2:
+ default:
+ nes_debug(NES_DBG_CM, "Received ack from unknown state: %x\n",
+ cm_node->state);
+ send_reset(cm_node);
+ break;
+ }
+ }
+
+ if (tcph->syn) {
+ if (cm_node->state == NES_CM_STATE_LISTENING) {
+ /* do not exceed backlog */
+ atomic_inc(&cm_node->listener->pend_accepts_cnt);
+ if (atomic_read(&cm_node->listener->pend_accepts_cnt) >
+ cm_node->listener->backlog) {
+ nes_debug(NES_DBG_CM, "drop syn due to backlog pressure \n");
+ cm_backlog_drops++;
+ atomic_dec(&cm_node->listener->pend_accepts_cnt);
+ rem_ref_cm_node(cm_core, cm_node);
+ return 0;
+ }
+ cm_node->accept_pend = 1;
+
+ }
+ if (datasize == 0)
+ cm_node->tcp_cntxt.rcv_nxt ++;
+
+ if (cm_node->state == NES_CM_STATE_LISTENING) {
+ cm_node->state = NES_CM_STATE_SYN_RCVD;
+ send_syn(cm_node, 1);
+ }
+ if (cm_node->state == NES_CM_STATE_ONE_SIDE_ESTABLISHED) {
+ cm_node->state = NES_CM_STATE_ESTABLISHED;
+ /* send final handshake ACK */
+ ret = send_ack(cm_node);
+ if (ret < 0)
+ return ret;
+
+ cm_node->state = NES_CM_STATE_MPAREQ_SENT;
+ ret = send_mpa_request(cm_node);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (tcph->fin) {
+ cm_node->tcp_cntxt.rcv_nxt++;
+ switch (cm_node->state) {
+ case NES_CM_STATE_SYN_RCVD:
+ case NES_CM_STATE_SYN_SENT:
+ case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
+ case NES_CM_STATE_ESTABLISHED:
+ case NES_CM_STATE_ACCEPTING:
+ case NES_CM_STATE_MPAREQ_SENT:
+ cm_node->state = NES_CM_STATE_CLOSE_WAIT;
+ cm_node->state = NES_CM_STATE_LAST_ACK;
+ ret = send_fin(cm_node, NULL);
+ break;
+ case NES_CM_STATE_FIN_WAIT1:
+ cm_node->state = NES_CM_STATE_CLOSING;
+ ret = send_ack(cm_node);
+ break;
+ case NES_CM_STATE_FIN_WAIT2:
+ cm_node->state = NES_CM_STATE_TIME_WAIT;
+ cm_node->tcp_cntxt.loc_seq_num ++;
+ ret = send_ack(cm_node);
+ /* need to schedule this to happen in 2MSL timeouts */
+ cm_node->state = NES_CM_STATE_CLOSED;
+ break;
+ case NES_CM_STATE_CLOSE_WAIT:
+ case NES_CM_STATE_LAST_ACK:
+ case NES_CM_STATE_CLOSING:
+ case NES_CM_STATE_TSA:
+ default:
+ nes_debug(NES_DBG_CM, "Received a fin while in %x state\n",
+ cm_node->state);
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ if (datasize) {
+ u8 *dataloc = skb->data;
+ /* figure out what state we are in and handle transition to next state */
+ switch (cm_node->state) {
+ case NES_CM_STATE_LISTENING:
+ case NES_CM_STATE_SYN_RCVD:
+ case NES_CM_STATE_SYN_SENT:
+ case NES_CM_STATE_FIN_WAIT1:
+ case NES_CM_STATE_FIN_WAIT2:
+ case NES_CM_STATE_CLOSE_WAIT:
+ case NES_CM_STATE_LAST_ACK:
+ case NES_CM_STATE_CLOSING:
+ break;
+ case NES_CM_STATE_MPAREQ_SENT:
+ /* recv the mpa res frame, ret=frame len (incl priv data) */
+ ret = parse_mpa(cm_node, dataloc, datasize);
+ if (ret < 0)
+ break;
+ /* set the req frame payload len in skb */
+ /* we are done handling this state, set node to a TSA state */
+ cm_node->state = NES_CM_STATE_TSA;
+ send_ack(cm_node);
+ create_event(cm_node, NES_CM_EVENT_CONNECTED);
+ break;
+
+ case NES_CM_STATE_ESTABLISHED:
+ /* we are expecting an MPA req frame */
+ ret = parse_mpa(cm_node, dataloc, datasize);
+ if (ret < 0) {
+ break;
+ }
+ cm_node->state = NES_CM_STATE_TSA;
+ send_ack(cm_node);
+ /* we got a valid MPA request, create an event */
+ create_event(cm_node, NES_CM_EVENT_MPA_REQ);
+ break;
+ case NES_CM_STATE_TSA:
+ handle_exception_pkt(cm_node, skb);
+ break;
+ case NES_CM_STATE_UNKNOWN:
+ case NES_CM_STATE_INITED:
+ default:
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * mini_cm_listen - create a listen node with params
+ */
+static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
+ struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
+{
+ struct nes_cm_listener *listener;
+ unsigned long flags;
+
+ nes_debug(NES_DBG_CM, "Search for 0x%08x : 0x%04x\n",
+ cm_info->loc_addr, cm_info->loc_port);
+
+ /* cannot have multiple matching listeners */
+ listener = find_listener(cm_core, htonl(cm_info->loc_addr),
+ htons(cm_info->loc_port), NES_CM_LISTENER_EITHER_STATE);
+ if (listener && listener->listener_state == NES_CM_LISTENER_ACTIVE_STATE) {
+ /* find automatically incs ref count ??? */
+ atomic_dec(&listener->ref_count);
+ nes_debug(NES_DBG_CM, "Not creating listener since it already exists\n");
+ return NULL;
+ }
+
+ if (!listener) {
+ /* create a CM listen node (1/2 node to compare incoming traffic to) */
+ listener = kzalloc(sizeof(*listener), GFP_ATOMIC);
+ if (!listener) {
+ nes_debug(NES_DBG_CM, "Not creating listener memory allocation failed\n");
+ return NULL;
+ }
+
+ memset(listener, 0, sizeof(struct nes_cm_listener));
+ listener->loc_addr = htonl(cm_info->loc_addr);
+ listener->loc_port = htons(cm_info->loc_port);
+ listener->reused_node = 0;
+
+ atomic_set(&listener->ref_count, 1);
+ }
+ /* pasive case */
+ /* find already inc'ed the ref count */
+ else {
+ listener->reused_node = 1;
+ }
+
+ listener->cm_id = cm_info->cm_id;
+ atomic_set(&listener->pend_accepts_cnt, 0);
+ listener->cm_core = cm_core;
+ listener->nesvnic = nesvnic;
+ atomic_inc(&cm_core->node_cnt);
+ atomic_inc(&cm_core->session_id);
+
+ listener->session_id = (u32)(atomic_read(&cm_core->session_id) + current->tgid);
+ listener->conn_type = cm_info->conn_type;
+ listener->backlog = cm_info->backlog;
+ listener->listener_state = NES_CM_LISTENER_ACTIVE_STATE;
+
+ if (!listener->reused_node) {
+ spin_lock_irqsave(&cm_core->listen_list_lock, flags);
+ list_add(&listener->list, &cm_core->listen_list.list);
+ spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
+ atomic_inc(&cm_core->listen_node_cnt);
+ }
+
+ nes_debug(NES_DBG_CM, "Api - listen(): addr=0x%08X, port=0x%04x,"
+ " listener = %p, backlog = %d, cm_id = %p.\n",
+ cm_info->loc_addr, cm_info->loc_port,
+ listener, listener->backlog, listener->cm_id);
+
+ return listener;
+}
+
+
+/**
+ * mini_cm_connect - make a connection node with params
+ */
+struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
+ struct nes_vnic *nesvnic, struct ietf_mpa_frame *mpa_frame,
+ struct nes_cm_info *cm_info)
+{
+ int ret = 0;
+ struct nes_cm_node *cm_node;
+ struct nes_cm_listener *loopbackremotelistener;
+ struct nes_cm_node *loopbackremotenode;
+ struct nes_cm_info loopback_cm_info;
+
+ u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) +
+ ntohs(mpa_frame->priv_data_len);
+
+ cm_info->loc_addr = htonl(cm_info->loc_addr);
+ cm_info->rem_addr = htonl(cm_info->rem_addr);
+ cm_info->loc_port = htons(cm_info->loc_port);
+ cm_info->rem_port = htons(cm_info->rem_port);
+
+ /* create a CM connection node */
+ cm_node = make_cm_node(cm_core, nesvnic, cm_info, NULL);
+ if (!cm_node)
+ return NULL;
+
+ // set our node side to client (active) side
+ cm_node->tcp_cntxt.client = 1;
+ cm_node->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
+
+ if (cm_info->loc_addr == cm_info->rem_addr) {
+ loopbackremotelistener = find_listener(cm_core, cm_node->rem_addr,
+ cm_node->rem_port, NES_CM_LISTENER_ACTIVE_STATE);
+ if (loopbackremotelistener == NULL) {
+ create_event(cm_node, NES_CM_EVENT_ABORTED);
+ } else {
+ atomic_inc(&cm_loopbacks);
+ loopback_cm_info = *cm_info;
+ loopback_cm_info.loc_port = cm_info->rem_port;
+ loopback_cm_info.rem_port = cm_info->loc_port;
+ loopback_cm_info.cm_id = loopbackremotelistener->cm_id;
+ loopbackremotenode = make_cm_node(cm_core, nesvnic, &loopback_cm_info,
+ loopbackremotelistener);
+ loopbackremotenode->loopbackpartner = cm_node;
+ loopbackremotenode->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
+ cm_node->loopbackpartner = loopbackremotenode;
+ memcpy(loopbackremotenode->mpa_frame_buf, &mpa_frame->priv_data,
+ mpa_frame_size);
+ loopbackremotenode->mpa_frame_size = mpa_frame_size -
+ sizeof(struct ietf_mpa_frame);
+
+ // we are done handling this state, set node to a TSA state
+ cm_node->state = NES_CM_STATE_TSA;
+ cm_node->tcp_cntxt.rcv_nxt = loopbackremotenode->tcp_cntxt.loc_seq_num;
+ loopbackremotenode->tcp_cntxt.rcv_nxt = cm_node->tcp_cntxt.loc_seq_num;
+ cm_node->tcp_cntxt.max_snd_wnd = loopbackremotenode->tcp_cntxt.rcv_wnd;
+ loopbackremotenode->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.rcv_wnd;
+ cm_node->tcp_cntxt.snd_wnd = loopbackremotenode->tcp_cntxt.rcv_wnd;
+ loopbackremotenode->tcp_cntxt.snd_wnd = cm_node->tcp_cntxt.rcv_wnd;
+ cm_node->tcp_cntxt.snd_wscale = loopbackremotenode->tcp_cntxt.rcv_wscale;
+ loopbackremotenode->tcp_cntxt.snd_wscale = cm_node->tcp_cntxt.rcv_wscale;
+
+ create_event(loopbackremotenode, NES_CM_EVENT_MPA_REQ);
+ }
+ return cm_node;
+ }
+
+ /* set our node side to client (active) side */
+ cm_node->tcp_cntxt.client = 1;
+ /* init our MPA frame ptr */
+ memcpy(&cm_node->mpa_frame, mpa_frame, mpa_frame_size);
+ cm_node->mpa_frame_size = mpa_frame_size;
+
+ /* send a syn and goto syn sent state */
+ cm_node->state = NES_CM_STATE_SYN_SENT;
+ ret = send_syn(cm_node, 0);
+
+ nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X, port=0x%04x,"
+ " cm_node=%p, cm_id = %p.\n",
+ cm_node->rem_addr, cm_node->rem_port, cm_node, cm_node->cm_id);
+
+ return cm_node;
+}
+
+
+/**
+ * mini_cm_accept - accept a connection
+ * This function is never called
+ */
+int mini_cm_accept(struct nes_cm_core *cm_core, struct ietf_mpa_frame *mpa_frame,
+ struct nes_cm_node *cm_node)
+{
+ return 0;
+}
+
+
+/**
+ * mini_cm_reject - reject and teardown a connection
+ */
+int mini_cm_reject(struct nes_cm_core *cm_core,
+ struct ietf_mpa_frame *mpa_frame,
+ struct nes_cm_node *cm_node)
+{
+ int ret = 0;
+ struct sk_buff *skb;
+ u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) +
+ ntohs(mpa_frame->priv_data_len);
+
+ skb = get_free_pkt(cm_node);
+ if (!skb) {
+ nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+ return -1;
+ }
+
+ /* send an MPA Request frame */
+ form_cm_frame(skb, cm_node, NULL, 0, mpa_frame, mpa_frame_size, SET_ACK | SET_FIN);
+ ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
+
+ cm_node->state = NES_CM_STATE_CLOSED;
+ ret = send_fin(cm_node, NULL);
+
+ if (ret < 0) {
+ printk(KERN_INFO PFX "failed to send MPA Reply (reject)\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+
+/**
+ * mini_cm_close
+ */
+int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
+{
+ int ret = 0;
+
+ if (!cm_core || !cm_node)
+ return -EINVAL;
+
+ switch (cm_node->state) {
+ /* if passed in node is null, create a reference key node for node search */
+ /* check if we found an owner node for this pkt */
+ case NES_CM_STATE_SYN_RCVD:
+ case NES_CM_STATE_SYN_SENT:
+ case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
+ case NES_CM_STATE_ESTABLISHED:
+ case NES_CM_STATE_ACCEPTING:
+ case NES_CM_STATE_MPAREQ_SENT:
+ cm_node->state = NES_CM_STATE_FIN_WAIT1;
+ send_fin(cm_node, NULL);
+ break;
+ case NES_CM_STATE_CLOSE_WAIT:
+ cm_node->state = NES_CM_STATE_LAST_ACK;
+ send_fin(cm_node, NULL);
+ break;
+ case NES_CM_STATE_FIN_WAIT1:
+ case NES_CM_STATE_FIN_WAIT2:
+ case NES_CM_STATE_LAST_ACK:
+ case NES_CM_STATE_TIME_WAIT:
+ case NES_CM_STATE_CLOSING:
+ ret = -1;
+ break;
+ case NES_CM_STATE_LISTENING:
+ case NES_CM_STATE_UNKNOWN:
+ case NES_CM_STATE_INITED:
+ case NES_CM_STATE_CLOSED:
+ case NES_CM_STATE_TSA:
+ ret = rem_ref_cm_node(cm_core, cm_node);
+ break;
+ }
+ cm_node->cm_id = NULL;
+ return ret;
+}
+
+
+/**
+ * recv_pkt - recv an ETHERNET packet, and process it through CM
+ * node state machine
+ */
+int mini_cm_recv_pkt(struct nes_cm_core *cm_core, struct nes_vnic *nesvnic,
+ struct sk_buff *skb)
+{
+ struct nes_cm_node *cm_node = NULL;
+ struct nes_cm_listener *listener = NULL;
+ struct iphdr *iph;
+ struct tcphdr *tcph;
+ struct nes_cm_info nfo;
+ int ret = 0;
+
+ if (!skb || skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ iph = (struct iphdr *)skb->data;
+ tcph = (struct tcphdr *)(skb->data + sizeof(struct iphdr));
+ skb_reset_network_header(skb);
+ skb_set_transport_header(skb, sizeof(*tcph));
+ skb->len = ntohs(iph->tot_len);
+
+ nfo.loc_addr = ntohl(iph->daddr);
+ nfo.loc_port = ntohs(tcph->dest);
+ nfo.rem_addr = ntohl(iph->saddr);
+ nfo.rem_port = ntohs(tcph->source);
+
+ nes_debug(NES_DBG_CM, "Received packet: dest=0x%08X:0x%04X src=0x%08X:0x%04X\n",
+ iph->daddr, tcph->dest, iph->saddr, tcph->source);
+
+ /* note: this call is going to increment cm_node ref count */
+ cm_node = find_node(cm_core,
+ nfo.rem_port, nfo.rem_addr,
+ nfo.loc_port, nfo.loc_addr);
+
+ if (!cm_node) {
+ listener = find_listener(cm_core, nfo.loc_addr, nfo.loc_port,
+ NES_CM_LISTENER_ACTIVE_STATE);
+ if (listener) {
+ nfo.cm_id = listener->cm_id;
+ nfo.conn_type = listener->conn_type;
+ } else {
+ nfo.cm_id = NULL;
+ nfo.conn_type = 0;
+ }
+
+ cm_node = make_cm_node(cm_core, nesvnic, &nfo, listener);
+ if (!cm_node) {
+ nes_debug(NES_DBG_CM, "Unable to allocate node\n");
+ if (listener) {
+ nes_debug(NES_DBG_CM, "unable to allocate node and decrementing listener refcount\n");
+ atomic_dec(&listener->ref_count);
+ }
+ ret = -1;
+ goto out;
+ }
+ if (!listener) {
+ nes_debug(NES_DBG_CM, "Packet found for unknown port %x refcnt=%d\n",
+ nfo.loc_port, atomic_read(&cm_node->ref_count));
+ if (!tcph->rst) {
+ nes_debug(NES_DBG_CM, "Packet found for unknown port=%d"
+ " rem_port=%d refcnt=%d\n",
+ nfo.loc_port, nfo.rem_port, atomic_read(&cm_node->ref_count));
+
+ cm_node->tcp_cntxt.rcv_nxt = ntohl(tcph->seq);
+ cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
+ send_reset(cm_node);
+ }
+ rem_ref_cm_node(cm_core, cm_node);
+ ret = -1;
+ goto out;
+ }
+ add_ref_cm_node(cm_node);
+ cm_node->state = NES_CM_STATE_LISTENING;
+ }
+
+ nes_debug(NES_DBG_CM, "Processing Packet for node %p, data = (%p):\n",
+ cm_node, skb->data);
+ process_packet(cm_node, skb, cm_core);
+
+ rem_ref_cm_node(cm_core, cm_node);
+ out:
+ if (skb)
+ dev_kfree_skb_any(skb);
+ return ret;
+}
+
+
+/**
+ * nes_cm_alloc_core - allocate a top level instance of a cm core
+ */
+struct nes_cm_core *nes_cm_alloc_core(void)
+{
+ int i;
+
+ struct nes_cm_core *cm_core;
+ struct sk_buff *skb = NULL;
+
+ /* setup the CM core */
+ /* alloc top level core control structure */
+ cm_core = kzalloc(sizeof(*cm_core), GFP_KERNEL);
+ if (!cm_core)
+ return NULL;
+
+ INIT_LIST_HEAD(&cm_core->connected_nodes);
+ init_timer(&cm_core->tcp_timer);
+ cm_core->tcp_timer.function = nes_cm_timer_tick;
+
+ cm_core->mtu = NES_CM_DEFAULT_MTU;
+ cm_core->state = NES_CM_STATE_INITED;
+ cm_core->free_tx_pkt_max = NES_CM_DEFAULT_FREE_PKTS;
+
+ atomic_set(&cm_core->session_id, 0);
+ atomic_set(&cm_core->events_posted, 0);
+
+ /* init the packet lists */
+ skb_queue_head_init(&cm_core->tx_free_list);
+
+ for (i = 0; i < NES_CM_DEFAULT_FRAME_CNT; i++) {
+ skb = dev_alloc_skb(cm_core->mtu);
+ if (!skb) {
+ kfree(cm_core);
+ return NULL;
+ }
+ /* add 'raw' skb to free frame list */
+ skb_queue_head(&cm_core->tx_free_list, skb);
+ }
+
+ cm_core->api = &nes_cm_api;
+
+ spin_lock_init(&cm_core->ht_lock);
+ spin_lock_init(&cm_core->listen_list_lock);
+
+ INIT_LIST_HEAD(&cm_core->listen_list.list);
+
+ nes_debug(NES_DBG_CM, "Init CM Core completed -- cm_core=%p\n", cm_core);
+
+ nes_debug(NES_DBG_CM, "Enable QUEUE EVENTS\n");
+ cm_core->event_wq = create_singlethread_workqueue("nesewq");
+ cm_core->post_event = nes_cm_post_event;
+ nes_debug(NES_DBG_CM, "Enable QUEUE DISCONNECTS\n");
+ cm_core->disconn_wq = create_singlethread_workqueue("nesdwq");
+
+ print_core(cm_core);
+ return cm_core;
+}
+
+
+/**
+ * mini_cm_dealloc_core - deallocate a top level instance of a cm core
+ */
+int mini_cm_dealloc_core(struct nes_cm_core *cm_core)
+{
+ nes_debug(NES_DBG_CM, "De-Alloc CM Core (%p)\n", cm_core);
+
+ if (!cm_core)
+ return -EINVAL;
+
+ barrier();
+
+ if (timer_pending(&cm_core->tcp_timer)) {
+ del_timer(&cm_core->tcp_timer);
+ }
+
+ destroy_workqueue(cm_core->event_wq);
+ destroy_workqueue(cm_core->disconn_wq);
+ nes_debug(NES_DBG_CM, "\n");
+ kfree(cm_core);
+
+ return 0;
+}
+
+
+/**
+ * mini_cm_get
+ */
+int mini_cm_get(struct nes_cm_core *cm_core)
+{
+ return cm_core->state;
+}
+
+
+/**
+ * mini_cm_set
+ */
+int mini_cm_set(struct nes_cm_core *cm_core, u32 type, u32 value)
+{
+ int ret = 0;
+
+ switch (type) {
+ case NES_CM_SET_PKT_SIZE:
+ cm_core->mtu = value;
+ break;
+ case NES_CM_SET_FREE_PKT_Q_SIZE:
+ cm_core->free_tx_pkt_max = value;
+ break;
+ default:
+ /* unknown set option */
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+
+/**
+ * nes_cm_init_tsa_conn setup HW; MPA frames must be
+ * successfully exchanged when this is called
+ */
+static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_node)
+{
+ int ret = 0;
+
+ if (!nesqp)
+ return -EINVAL;
+
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_IPV4 |
+ NES_QPCONTEXT_MISC_NO_NAGLE | NES_QPCONTEXT_MISC_DO_NOT_FRAG |
+ NES_QPCONTEXT_MISC_DROS);
+
+ if (cm_node->tcp_cntxt.snd_wscale || cm_node->tcp_cntxt.rcv_wscale)
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_WSCALE);
+
+ nesqp->nesqp_context->misc2 |= cpu_to_le32(64 << NES_QPCONTEXT_MISC2_TTL_SHIFT);
+
+ nesqp->nesqp_context->mss |= cpu_to_le32(((u32)cm_node->tcp_cntxt.mss) << 16);
+
+ nesqp->nesqp_context->tcp_state_flow_label |= cpu_to_le32(
+ (u32)NES_QPCONTEXT_TCPSTATE_EST << NES_QPCONTEXT_TCPFLOW_TCP_STATE_SHIFT);
+
+ nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32(
+ (cm_node->tcp_cntxt.snd_wscale << NES_QPCONTEXT_PDWSCALE_SND_WSCALE_SHIFT) &
+ NES_QPCONTEXT_PDWSCALE_SND_WSCALE_MASK);
+
+ nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32(
+ (cm_node->tcp_cntxt.rcv_wscale << NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_SHIFT) &
+ NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_MASK);
+
+ nesqp->nesqp_context->keepalive = cpu_to_le32(0x80);
+ nesqp->nesqp_context->ts_recent = 0;
+ nesqp->nesqp_context->ts_age = 0;
+ nesqp->nesqp_context->snd_nxt = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
+ nesqp->nesqp_context->snd_wnd = cpu_to_le32(cm_node->tcp_cntxt.snd_wnd);
+ nesqp->nesqp_context->rcv_nxt = cpu_to_le32(cm_node->tcp_cntxt.rcv_nxt);
+ nesqp->nesqp_context->rcv_wnd = cpu_to_le32(cm_node->tcp_cntxt.rcv_wnd <<
+ cm_node->tcp_cntxt.rcv_wscale);
+ nesqp->nesqp_context->snd_max = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
+ nesqp->nesqp_context->snd_una = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
+ nesqp->nesqp_context->srtt = 0;
+ nesqp->nesqp_context->rttvar = cpu_to_le32(0x6);
+ nesqp->nesqp_context->ssthresh = cpu_to_le32(0x3FFFC000);
+ nesqp->nesqp_context->cwnd = cpu_to_le32(2*cm_node->tcp_cntxt.mss);
+ nesqp->nesqp_context->snd_wl1 = cpu_to_le32(cm_node->tcp_cntxt.rcv_nxt);
+ nesqp->nesqp_context->snd_wl2 = cpu_to_le32(cm_node->tcp_cntxt.loc_seq_num);
+ nesqp->nesqp_context->max_snd_wnd = cpu_to_le32(cm_node->tcp_cntxt.max_snd_wnd);
+
+ nes_debug(NES_DBG_CM, "QP%u: rcv_nxt = 0x%08X, snd_nxt = 0x%08X,"
+ " Setting MSS to %u, PDWscale = 0x%08X, rcv_wnd = %u, context misc = 0x%08X.\n",
+ nesqp->hwqp.qp_id, le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
+ le32_to_cpu(nesqp->nesqp_context->snd_nxt),
+ cm_node->tcp_cntxt.mss, le32_to_cpu(nesqp->nesqp_context->pd_index_wscale),
+ le32_to_cpu(nesqp->nesqp_context->rcv_wnd),
+ le32_to_cpu(nesqp->nesqp_context->misc));
+ nes_debug(NES_DBG_CM, " snd_wnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->snd_wnd));
+ nes_debug(NES_DBG_CM, " snd_cwnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->cwnd));
+ nes_debug(NES_DBG_CM, " max_swnd = 0x%08X.\n", le32_to_cpu(nesqp->nesqp_context->max_snd_wnd));
+
+ nes_debug(NES_DBG_CM, "Change cm_node state to TSA\n");
+ cm_node->state = NES_CM_STATE_TSA;
+
+ return ret;
+}
+
+
+/**
+ * nes_cm_disconn
+ */
+int nes_cm_disconn(struct nes_qp *nesqp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nesqp->lock, flags);
+ if (nesqp->disconn_pending == 0) {
+ nesqp->disconn_pending++;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ /* nes_add_ref(&nesqp->ibqp); */
+ /* init our disconnect work element, to */
+ INIT_WORK(&nesqp->disconn_work, nes_disconnect_worker);
+
+ queue_work(g_cm_core->disconn_wq, &nesqp->disconn_work);
+ } else {
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_rem_ref(&nesqp->ibqp);
+ }
+
+ return 0;
+}
+
+
+/**
+ * nes_disconnect_worker
+ */
+void nes_disconnect_worker(struct work_struct *work)
+{
+ struct nes_qp *nesqp = container_of(work, struct nes_qp, disconn_work);
+
+ nes_debug(NES_DBG_CM, "processing AEQE id 0x%04X for QP%u.\n",
+ nesqp->last_aeq, nesqp->hwqp.qp_id);
+ nes_cm_disconn_true(nesqp);
+}
+
+
+/**
+ * nes_cm_disconn_true
+ */
+int nes_cm_disconn_true(struct nes_qp *nesqp)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct iw_cm_id *cm_id;
+ struct iw_cm_event cm_event;
+ struct nes_vnic *nesvnic;
+ u16 last_ae;
+ u8 original_hw_tcp_state;
+ u8 original_ibqp_state;
+ u8 issued_disconnect_reset = 0;
+
+ if (!nesqp) {
+ nes_debug(NES_DBG_CM, "disconnect_worker nesqp is NULL\n");
+ return -1;
+ }
+
+ spin_lock_irqsave(&nesqp->lock, flags);
+ cm_id = nesqp->cm_id;
+ /* make sure we havent already closed this connection */
+ if (!cm_id) {
+ nes_debug(NES_DBG_CM, "QP%u disconnect_worker cmid is NULL\n",
+ nesqp->hwqp.qp_id);
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -1;
+ }
+
+ nesvnic = to_nesvnic(nesqp->ibqp.device);
+ nes_debug(NES_DBG_CM, "Disconnecting QP%u\n", nesqp->hwqp.qp_id);
+
+ original_hw_tcp_state = nesqp->hw_tcp_state;
+ original_ibqp_state = nesqp->ibqp_state;
+ last_ae = nesqp->last_aeq;
+
+
+ nes_debug(NES_DBG_CM, "set ibqp_state=%u\n", nesqp->ibqp_state);
+
+ if ((nesqp->cm_id) && (cm_id->event_handler)) {
+ if ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
+ ((original_ibqp_state == IB_QPS_RTS) &&
+ (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+ atomic_inc(&cm_disconnects);
+ cm_event.event = IW_CM_EVENT_DISCONNECT;
+ if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) {
+ issued_disconnect_reset = 1;
+ cm_event.status = IW_CM_EVENT_STATUS_RESET;
+ nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event (status reset) for "
+ " QP%u, cm_id = %p. \n",
+ nesqp->hwqp.qp_id, cm_id);
+ } else {
+ cm_event.status = IW_CM_EVENT_STATUS_OK;
+ }
+
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+
+ nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event for "
+ " QP%u, SQ Head = %u, SQ Tail = %u. cm_id = %p, refcount = %u.\n",
+ nesqp->hwqp.qp_id,
+ nesqp->hwqp.sq_head, nesqp->hwqp.sq_tail, cm_id,
+ atomic_read(&nesqp->refcount));
+
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ if (ret)
+ nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ }
+
+ nesqp->disconn_pending = 0;
+ /* There might have been another AE while the lock was released */
+ original_hw_tcp_state = nesqp->hw_tcp_state;
+ original_ibqp_state = nesqp->ibqp_state;
+ last_ae = nesqp->last_aeq;
+
+ if ((issued_disconnect_reset == 0) && (nesqp->cm_id) &&
+ ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSED) ||
+ (original_hw_tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT) ||
+ (last_ae == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) ||
+ (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+ atomic_inc(&cm_closes);
+ nesqp->cm_id = NULL;
+ nesqp->in_disconnect = 0;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_disconnect(nesqp, 1);
+
+ cm_id->provider_data = nesqp;
+ /* Send up the close complete event */
+ cm_event.event = IW_CM_EVENT_CLOSE;
+ cm_event.status = IW_CM_EVENT_STATUS_OK;
+ cm_event.provider_data = cm_id->provider_data;
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ if (ret) {
+ nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
+ }
+
+ cm_id->rem_ref(cm_id);
+
+ spin_lock_irqsave(&nesqp->lock, flags);
+ if (nesqp->flush_issued == 0) {
+ nesqp->flush_issued = 1;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ flush_wqes(nesvnic->nesdev, nesqp, NES_CQP_FLUSH_RQ, 1);
+ } else {
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ }
+
+ /* This reference is from either ModifyQP or the AE processing,
+ there is still a race here with modifyqp */
+ nes_rem_ref(&nesqp->ibqp);
+
+ } else {
+ cm_id = nesqp->cm_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ /* check to see if the inbound reset beat the outbound reset */
+ if ((!cm_id) && (last_ae==NES_AEQE_AEID_RESET_SENT)) {
+ nes_debug(NES_DBG_CM, "QP%u: Decing refcount due to inbound reset"
+ " beating the outbound reset.\n",
+ nesqp->hwqp.qp_id);
+ nes_rem_ref(&nesqp->ibqp);
+ }
+ }
+ } else {
+ nesqp->disconn_pending = 0;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ }
+ nes_rem_ref(&nesqp->ibqp);
+
+ return 0;
+}
+
+
+/**
+ * nes_disconnect
+ */
+int nes_disconnect(struct nes_qp *nesqp, int abrupt)
+{
+ int ret = 0;
+ struct nes_vnic *nesvnic;
+ struct nes_device *nesdev;
+
+ nesvnic = to_nesvnic(nesqp->ibqp.device);
+ if (!nesvnic)
+ return -EINVAL;
+
+ nesdev = nesvnic->nesdev;
+
+ nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n",
+ atomic_read(&nesvnic->netdev->refcnt));
+
+ if (nesqp->active_conn) {
+
+ /* indicate this connection is NOT active */
+ nesqp->active_conn = 0;
+ } else {
+ /* Need to free the Last Streaming Mode Message */
+ if (nesqp->ietf_frame) {
+ pci_free_consistent(nesdev->pcidev,
+ nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
+ nesqp->ietf_frame, nesqp->ietf_frame_pbase);
+ }
+ }
+
+ /* close the CM node down if it is still active */
+ if (nesqp->cm_node) {
+ nes_debug(NES_DBG_CM, "Call close API\n");
+
+ g_cm_core->api->close(g_cm_core, nesqp->cm_node);
+ nesqp->cm_node = NULL;
+ }
+
+ return ret;
+}
+
+
+/**
+ * nes_accept
+ */
+int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
+{
+ u64 u64temp;
+ struct ib_qp *ibqp;
+ struct nes_qp *nesqp;
+ struct nes_vnic *nesvnic;
+ struct nes_device *nesdev;
+ struct nes_cm_node *cm_node;
+ struct nes_adapter *adapter;
+ struct ib_qp_attr attr;
+ struct iw_cm_event cm_event;
+ struct nes_hw_qp_wqe *wqe;
+ struct nes_v4_quad nes_quad;
+ int ret;
+
+ ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
+ if (!ibqp)
+ return -EINVAL;
+
+ /* get all our handles */
+ nesqp = to_nesqp(ibqp);
+ nesvnic = to_nesvnic(nesqp->ibqp.device);
+ nesdev = nesvnic->nesdev;
+ adapter = nesdev->nesadapter;
+
+ nes_debug(NES_DBG_CM, "nesvnic=%p, netdev=%p, %s\n",
+ nesvnic, nesvnic->netdev, nesvnic->netdev->name);
+
+ /* since this is from a listen, we were able to put node handle into cm_id */
+ cm_node = (struct nes_cm_node *)cm_id->provider_data;
+
+ /* associate the node with the QP */
+ nesqp->cm_node = (void *)cm_node;
+
+ nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu\n",
+ nesqp->hwqp.qp_id, cm_node, jiffies);
+ atomic_inc(&cm_accepts);
+
+ nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n",
+ atomic_read(&nesvnic->netdev->refcnt));
+
+ /* allocate the ietf frame and space for private data */
+ nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev,
+ sizeof(struct ietf_mpa_frame) + conn_param->private_data_len,
+ &nesqp->ietf_frame_pbase);
+
+ if (!nesqp->ietf_frame) {
+ nes_debug(NES_DBG_CM, "Unable to allocate memory for private data\n");
+ return -ENOMEM;
+ }
+
+
+ /* setup the MPA frame */
+ nesqp->private_data_len = conn_param->private_data_len;
+ memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
+
+ memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
+ conn_param->private_data_len);
+
+ nesqp->ietf_frame->priv_data_len = cpu_to_be16(conn_param->private_data_len);
+ nesqp->ietf_frame->rev = mpa_version;
+ nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
+
+ /* setup our first outgoing iWarp send WQE (the IETF frame response) */
+ wqe = &nesqp->hwqp.sq_vbase[0];
+
+ if (cm_id->remote_addr.sin_addr.s_addr != cm_id->local_addr.sin_addr.s_addr) {
+ u64temp = (unsigned long)nesqp;
+ u64temp |= NES_SW_CONTEXT_ALIGN>>1;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
+ u64temp);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+ cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING | NES_IWARP_SQ_WQE_WRPDU);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
+ cpu_to_le32(conn_param->private_data_len + sizeof(struct ietf_mpa_frame));
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] =
+ cpu_to_le32((u32)nesqp->ietf_frame_pbase);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] =
+ cpu_to_le32((u32)((u64)nesqp->ietf_frame_pbase >> 32));
+ wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] =
+ cpu_to_le32(conn_param->private_data_len + sizeof(struct ietf_mpa_frame));
+ wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+
+ nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(
+ NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | NES_QPCONTEXT_ORDIRD_WRPDU);
+ } else {
+ nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+ NES_QPCONTEXT_ORDIRD_WRPDU | NES_QPCONTEXT_ORDIRD_ALSMM));
+ }
+ nesqp->skip_lsmm = 1;
+
+
+ /* Cache the cm_id in the qp */
+ nesqp->cm_id = cm_id;
+ cm_node->cm_id = cm_id;
+
+ /* nesqp->cm_node = (void *)cm_id->provider_data; */
+ cm_id->provider_data = nesqp;
+ nesqp->active_conn = 0;
+
+ nes_cm_init_tsa_conn(nesqp, cm_node);
+
+ nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(cm_id->local_addr.sin_port));
+ nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(cm_id->remote_addr.sin_port));
+ nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
+
+ nesqp->nesqp_context->misc2 |= cpu_to_le32(
+ (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
+
+ nesqp->nesqp_context->arp_index_vlan |= cpu_to_le32(
+ nes_arp_table(nesdev, le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
+ NES_ARP_RESOLVE) << 16);
+
+ nesqp->nesqp_context->ts_val_delta = cpu_to_le32(
+ jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
+
+ nesqp->nesqp_context->ird_index = cpu_to_le32(nesqp->hwqp.qp_id);
+
+ nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(
+ ((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT));
+ nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
+
+ memset(&nes_quad, 0, sizeof(nes_quad));
+ nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
+ nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr;
+ nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port;
+ nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port;
+
+ /* Produce hash key */
+ nesqp->hte_index = cpu_to_be32(
+ crc32c(~0, (void *)&nes_quad, sizeof(nes_quad)) ^ 0xffffffff);
+ nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, CRC = 0x%08X\n",
+ nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
+
+ nesqp->hte_index &= adapter->hte_index_mask;
+ nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
+
+ cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
+
+ nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = 0x%08X:0x%04X,"
+ " rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + private data length=%zu.\n",
+ nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohl(cm_id->local_addr.sin_addr.s_addr),
+ ntohs(cm_id->local_addr.sin_port),
+ le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
+ le32_to_cpu(nesqp->nesqp_context->snd_nxt),
+ conn_param->private_data_len+sizeof(struct ietf_mpa_frame));
+
+ attr.qp_state = IB_QPS_RTS;
+ nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
+
+ /* notify OF layer that accept event was successfull */
+ cm_id->add_ref(cm_id);
+
+ cm_event.event = IW_CM_EVENT_ESTABLISHED;
+ cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
+ cm_event.provider_data = (void *)nesqp;
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ if (cm_node->loopbackpartner) {
+ cm_node->loopbackpartner->mpa_frame_size = nesqp->private_data_len;
+ /* copy entire MPA frame to our cm_node's frame */
+ memcpy(cm_node->loopbackpartner->mpa_frame_buf, nesqp->ietf_frame->priv_data,
+ nesqp->private_data_len);
+ create_event(cm_node->loopbackpartner, NES_CM_EVENT_CONNECTED);
+ }
+ if (ret)
+ printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
+ __FUNCTION__, __LINE__, ret);
+
+ return 0;
+}
+
+
+/**
+ * nes_reject
+ */
+int nes_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
+{
+ struct nes_cm_node *cm_node;
+ struct nes_cm_core *cm_core;
+
+ atomic_inc(&cm_rejects);
+ cm_node = (struct nes_cm_node *) cm_id->provider_data;
+ cm_core = cm_node->cm_core;
+ cm_node->mpa_frame_size = sizeof(struct ietf_mpa_frame) + pdata_len;
+
+ strcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP);
+ memcpy(&cm_node->mpa_frame.priv_data, pdata, pdata_len);
+
+ cm_node->mpa_frame.priv_data_len = cpu_to_be16(pdata_len);
+ cm_node->mpa_frame.rev = mpa_version;
+ cm_node->mpa_frame.flags = IETF_MPA_FLAGS_CRC | IETF_MPA_FLAGS_REJECT;
+
+ cm_core->api->reject(cm_core, &cm_node->mpa_frame, cm_node);
+
+ return 0;
+}
+
+
+/**
+ * nes_connect
+ * setup and launch cm connect node
+ */
+int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
+{
+ struct ib_qp *ibqp;
+ struct nes_qp *nesqp;
+ struct nes_vnic *nesvnic;
+ struct nes_device *nesdev;
+ struct nes_cm_node *cm_node;
+ struct nes_cm_info cm_info;
+
+ ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
+ if (!ibqp)
+ return -EINVAL;
+ nesqp = to_nesqp(ibqp);
+ if (!nesqp)
+ return -EINVAL;
+ nesvnic = to_nesvnic(nesqp->ibqp.device);
+ if (!nesvnic)
+ return -EINVAL;
+ nesdev = nesvnic->nesdev;
+ if (!nesdev)
+ return -EINVAL;
+
+ atomic_inc(&cm_connects);
+
+ nesqp->ietf_frame = kzalloc(sizeof(struct ietf_mpa_frame) +
+ conn_param->private_data_len, GFP_KERNEL);
+ if (!nesqp->ietf_frame)
+ return -ENOMEM;
+
+ /* set qp as having an active connection */
+ nesqp->active_conn = 1;
+
+ nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = 0x%08X:0x%04X.\n",
+ nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohl(cm_id->local_addr.sin_addr.s_addr),
+ ntohs(cm_id->local_addr.sin_port));
+
+ /* cache the cm_id in the qp */
+ nesqp->cm_id = cm_id;
+
+ cm_id->provider_data = nesqp;
+
+ /* copy the private data */
+ if (conn_param->private_data_len) {
+ memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
+ conn_param->private_data_len);
+ }
+
+ nesqp->private_data_len = conn_param->private_data_len;
+ nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
+ nes_debug(NES_DBG_CM, "requested ord = 0x%08X.\n", (u32)conn_param->ord);
+ nes_debug(NES_DBG_CM, "mpa private data len =%u\n", conn_param->private_data_len);
+
+ strcpy(&nesqp->ietf_frame->key[0], IEFT_MPA_KEY_REQ);
+ nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
+ nesqp->ietf_frame->rev = IETF_MPA_VERSION;
+ nesqp->ietf_frame->priv_data_len = htons(conn_param->private_data_len);
+
+ if (cm_id->local_addr.sin_addr.s_addr != cm_id->remote_addr.sin_addr.s_addr)
+ nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
+ PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+
+ /* set up the connection params for the node */
+ cm_info.loc_addr = (cm_id->local_addr.sin_addr.s_addr);
+ cm_info.loc_port = (cm_id->local_addr.sin_port);
+ cm_info.rem_addr = (cm_id->remote_addr.sin_addr.s_addr);
+ cm_info.rem_port = (cm_id->remote_addr.sin_port);
+ cm_info.cm_id = cm_id;
+ cm_info.conn_type = NES_CM_IWARP_CONN_TYPE;
+
+ cm_id->add_ref(cm_id);
+ nes_add_ref(&nesqp->ibqp);
+
+ /* create a connect CM node connection */
+ cm_node = g_cm_core->api->connect(g_cm_core, nesvnic, nesqp->ietf_frame, &cm_info);
+ if (!cm_node) {
+ if (cm_id->local_addr.sin_addr.s_addr != cm_id->remote_addr.sin_addr.s_addr)
+ nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
+ PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
+ nes_rem_ref(&nesqp->ibqp);
+ kfree(nesqp->ietf_frame);
+ nesqp->ietf_frame = NULL;
+ cm_id->rem_ref(cm_id);
+ return -ENOMEM;
+ }
+
+ cm_node->apbvt_set = 1;
+ nesqp->cm_node = cm_node;
+
+ return 0;
+}
+
+
+/**
+ * nes_create_listen
+ */
+int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
+{
+ struct nes_vnic *nesvnic;
+ struct nes_cm_listener *cm_node;
+ struct nes_cm_info cm_info;
+ struct nes_adapter *adapter;
+ int err;
+
+
+ nes_debug(NES_DBG_CM, "cm_id = %p, local port = 0x%04X.\n",
+ cm_id, ntohs(cm_id->local_addr.sin_port));
+
+ nesvnic = to_nesvnic(cm_id->device);
+ if (!nesvnic)
+ return -EINVAL;
+ adapter = nesvnic->nesdev->nesadapter;
+ nes_debug(NES_DBG_CM, "nesvnic=%p, netdev=%p, %s\n",
+ nesvnic, nesvnic->netdev, nesvnic->netdev->name);
+
+ nes_debug(NES_DBG_CM, "nesvnic->local_ipaddr=0x%08x, sin_addr.s_addr=0x%08x\n",
+ nesvnic->local_ipaddr, cm_id->local_addr.sin_addr.s_addr);
+
+ /* setup listen params in our api call struct */
+ cm_info.loc_addr = nesvnic->local_ipaddr;
+ cm_info.loc_port = cm_id->local_addr.sin_port;
+ cm_info.backlog = backlog;
+ cm_info.cm_id = cm_id;
+
+ cm_info.conn_type = NES_CM_IWARP_CONN_TYPE;
+
+
+ cm_node = g_cm_core->api->listen(g_cm_core, nesvnic, &cm_info);
+ if (!cm_node) {
+ printk("%s[%u] Error returned from listen API call\n",
+ __FUNCTION__, __LINE__);
+ return -ENOMEM;
+ }
+
+ cm_id->provider_data = cm_node;
+
+ if (!cm_node->reused_node) {
+ err = nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
+ PCI_FUNC(nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+ if (err) {
+ printk("nes_manage_apbvt call returned %d.\n", err);
+ g_cm_core->api->stop_listener(g_cm_core, (void *)cm_node);
+ return err;
+ }
+ cm_listens_created++;
+ }
+
+ cm_id->add_ref(cm_id);
+ cm_id->provider_data = (void *)cm_node;
+
+
+ return 0;
+}
+
+
+/**
+ * nes_destroy_listen
+ */
+int nes_destroy_listen(struct iw_cm_id *cm_id)
+{
+ if (cm_id->provider_data)
+ g_cm_core->api->stop_listener(g_cm_core, cm_id->provider_data);
+ else
+ nes_debug(NES_DBG_CM, "cm_id->provider_data was NULL\n");
+
+ cm_id->rem_ref(cm_id);
+
+ return 0;
+}
+
+
+/**
+ * nes_cm_recv
+ */
+int nes_cm_recv(struct sk_buff *skb, struct net_device *netdevice)
+{
+ cm_packets_received++;
+ if ((g_cm_core) && (g_cm_core->api)) {
+ g_cm_core->api->recv_pkt(g_cm_core, netdev_priv(netdevice), skb);
+ } else {
+ nes_debug(NES_DBG_CM, "Unable to process packet for CM,"
+ " cm is not setup properly.\n");
+ }
+
+ return 0;
+}
+
+
+/**
+ * nes_cm_start
+ * Start and init a cm core module
+ */
+int nes_cm_start(void)
+{
+ nes_debug(NES_DBG_CM, "\n");
+ /* create the primary CM core, pass this handle to subsequent core inits */
+ g_cm_core = nes_cm_alloc_core();
+ if (g_cm_core) {
+ return 0;
+ } else {
+ return -ENOMEM;
+ }
+}
+
+
+/**
+ * nes_cm_stop
+ * stop and dealloc all cm core instances
+ */
+int nes_cm_stop(void)
+{
+ g_cm_core->api->destroy_cm_core(g_cm_core);
+ return 0;
+}
+
+
+/**
+ * cm_event_connected
+ * handle a connected event, setup QPs and HW
+ */
+void cm_event_connected(struct nes_cm_event *event)
+{
+ u64 u64temp;
+ struct nes_qp *nesqp;
+ struct nes_vnic *nesvnic;
+ struct nes_device *nesdev;
+ struct nes_cm_node *cm_node;
+ struct nes_adapter *nesadapter;
+ struct ib_qp_attr attr;
+ struct iw_cm_id *cm_id;
+ struct iw_cm_event cm_event;
+ struct nes_hw_qp_wqe *wqe;
+ struct nes_v4_quad nes_quad;
+ int ret;
+
+ /* get all our handles */
+ cm_node = event->cm_node;
+ cm_id = cm_node->cm_id;
+ nes_debug(NES_DBG_CM, "cm_event_connected - %p - cm_id = %p\n", cm_node, cm_id);
+ nesqp = (struct nes_qp *)cm_id->provider_data;
+ nesvnic = to_nesvnic(nesqp->ibqp.device);
+ nesdev = nesvnic->nesdev;
+ nesadapter = nesdev->nesadapter;
+
+ if (nesqp->destroyed) {
+ return;
+ }
+ atomic_inc(&cm_connecteds);
+ nes_debug(NES_DBG_CM, "QP%u attempting to connect to 0x%08X:0x%04X on"
+ " local port 0x%04X. jiffies = %lu.\n",
+ nesqp->hwqp.qp_id,
+ ntohl(cm_id->remote_addr.sin_addr.s_addr),
+ ntohs(cm_id->remote_addr.sin_port),
+ ntohs(cm_id->local_addr.sin_port),
+ jiffies);
+
+ nes_cm_init_tsa_conn(nesqp, cm_node);
+
+ /* set the QP tsa context */
+ nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(cm_id->local_addr.sin_port));
+ nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(cm_id->remote_addr.sin_port));
+ nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
+
+ nesqp->nesqp_context->misc2 |= cpu_to_le32(
+ (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
+ nesqp->nesqp_context->arp_index_vlan |= cpu_to_le32(
+ nes_arp_table(nesdev, le32_to_cpu(nesqp->nesqp_context->ip0),
+ NULL, NES_ARP_RESOLVE) << 16);
+ nesqp->nesqp_context->ts_val_delta = cpu_to_le32(
+ jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
+ nesqp->nesqp_context->ird_index = cpu_to_le32(nesqp->hwqp.qp_id);
+ nesqp->nesqp_context->ird_ord_sizes |=
+ cpu_to_le32((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT);
+
+ /* Adjust tail for not having a LSMM */
+ nesqp->hwqp.sq_tail = 1;
+
+#if defined(NES_SEND_FIRST_WRITE)
+ if (cm_node->send_write0) {
+ nes_debug(NES_DBG_CM, "Sending first write.\n");
+ wqe = &nesqp->hwqp.sq_vbase[0];
+ u64temp = (unsigned long)nesqp;
+ u64temp |= NES_SW_CONTEXT_ALIGN>>1;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
+ u64temp);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
+ wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
+ wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+
+ /* use the reserved spot on the WQ for the extra first WQE */
+ nesqp->nesqp_context->ird_ord_sizes &= cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+ NES_QPCONTEXT_ORDIRD_WRPDU | NES_QPCONTEXT_ORDIRD_ALSMM));
+ nesqp->skip_lsmm = 1;
+ nesqp->hwqp.sq_tail = 0;
+ nes_write32(nesdev->regs + NES_WQE_ALLOC,
+ (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
+ }
+#endif
+
+ memset(&nes_quad, 0, sizeof(nes_quad));
+
+ nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
+ nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr;
+ nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port;
+ nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port;
+
+ /* Produce hash key */
+ nesqp->hte_index = cpu_to_be32(
+ crc32c(~0, (void *)&nes_quad, sizeof(nes_quad)) ^ 0xffffffff);
+ nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, After CRC = 0x%08X\n",
+ nesqp->hte_index, nesqp->hte_index & nesadapter->hte_index_mask);
+
+ nesqp->hte_index &= nesadapter->hte_index_mask;
+ nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
+
+ nesqp->ietf_frame = &cm_node->mpa_frame;
+ nesqp->private_data_len = (u8) cm_node->mpa_frame_size;
+ cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
+
+ /* modify QP state to rts */
+ attr.qp_state = IB_QPS_RTS;
+ nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
+
+ /* notify OF layer we successfully created the requested connection */
+ cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
+ cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
+ cm_event.provider_data = cm_id->provider_data;
+ cm_event.local_addr.sin_family = AF_INET;
+ cm_event.local_addr.sin_port = cm_id->local_addr.sin_port;
+ cm_event.remote_addr = cm_id->remote_addr;
+
+ cm_event.private_data = (void *)event->cm_node->mpa_frame_buf;
+ cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size;
+
+ cm_event.local_addr.sin_addr.s_addr = event->cm_info.rem_addr;
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
+
+ if (ret)
+ printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
+ __FUNCTION__, __LINE__, ret);
+ nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = %lu\n",
+ nesqp->hwqp.qp_id, jiffies );
+
+ nes_rem_ref(&nesqp->ibqp);
+
+ return;
+}
+
+
+/**
+ * cm_event_connect_error
+ */
+void cm_event_connect_error(struct nes_cm_event *event)
+{
+ struct nes_qp *nesqp;
+ struct iw_cm_id *cm_id;
+ struct iw_cm_event cm_event;
+ /* struct nes_cm_info cm_info; */
+ int ret;
+
+ if (!event->cm_node)
+ return;
+
+ cm_id = event->cm_node->cm_id;
+ if (!cm_id) {
+ return;
+ }
+
+ nes_debug(NES_DBG_CM, "cm_node=%p, cm_id=%p\n", event->cm_node, cm_id);
+ nesqp = cm_id->provider_data;
+
+ if (!nesqp) {
+ return;
+ }
+
+ /* notify OF layer about this connection error event */
+ /* cm_id->rem_ref(cm_id); */
+ nesqp->cm_id = NULL;
+ cm_id->provider_data = NULL;
+ cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
+ cm_event.status = IW_CM_EVENT_STATUS_REJECTED;
+ cm_event.provider_data = cm_id->provider_data;
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+
+ nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, remove_addr=%08x\n",
+ cm_event.local_addr.sin_addr.s_addr, cm_event.remote_addr.sin_addr.s_addr);
+
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
+ if (ret)
+ printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
+ __FUNCTION__, __LINE__, ret);
+ nes_rem_ref(&nesqp->ibqp);
+ cm_id->rem_ref(cm_id);
+
+ return;
+}
+
+
+/**
+ * cm_event_reset
+ */
+void cm_event_reset(struct nes_cm_event *event)
+{
+ struct nes_qp *nesqp;
+ struct iw_cm_id *cm_id;
+ struct iw_cm_event cm_event;
+ /* struct nes_cm_info cm_info; */
+ int ret;
+
+ if (!event->cm_node)
+ return;
+
+ if (!event->cm_node->cm_id)
+ return;
+
+ cm_id = event->cm_node->cm_id;
+
+ nes_debug(NES_DBG_CM, "%p - cm_id = %p\n", event->cm_node, cm_id);
+ nesqp = cm_id->provider_data;
+
+ nesqp->cm_id = NULL;
+ /* cm_id->provider_data = NULL; */
+ cm_event.event = IW_CM_EVENT_DISCONNECT;
+ cm_event.status = IW_CM_EVENT_STATUS_RESET;
+ cm_event.provider_data = cm_id->provider_data;
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
+
+
+ /* notify OF layer about this connection error event */
+ cm_id->rem_ref(cm_id);
+
+ return;
+}
+
+
+/**
+ * cm_event_mpa_req
+ */
+void cm_event_mpa_req(struct nes_cm_event *event)
+{
+ struct iw_cm_id *cm_id;
+ struct iw_cm_event cm_event;
+ int ret;
+ struct nes_cm_node *cm_node;
+
+ cm_node = event->cm_node;
+ if (!cm_node)
+ return;
+ cm_id = cm_node->cm_id;
+
+ atomic_inc(&cm_connect_reqs);
+ nes_debug(NES_DBG_CM, "cm_node = %p - cm_id = %p, jiffies = %lu\n",
+ cm_node, cm_id, jiffies);
+
+ cm_event.event = IW_CM_EVENT_CONNECT_REQUEST;
+ cm_event.status = IW_CM_EVENT_STATUS_OK;
+ cm_event.provider_data = (void *)cm_node;
+
+ cm_event.local_addr.sin_family = AF_INET;
+ cm_event.local_addr.sin_port = htons(event->cm_info.loc_port);
+ cm_event.local_addr.sin_addr.s_addr = htonl(event->cm_info.loc_addr);
+
+ cm_event.remote_addr.sin_family = AF_INET;
+ cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port);
+ cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr);
+
+ cm_event.private_data = cm_node->mpa_frame_buf;
+ cm_event.private_data_len = (u8) cm_node->mpa_frame_size;
+
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ if (ret)
+ printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
+ __FUNCTION__, __LINE__, ret);
+
+ return;
+}
+
+
+static void nes_cm_event_handler(struct work_struct *);
+
+/**
+ * nes_cm_post_event
+ * post an event to the cm event handler
+ */
+int nes_cm_post_event(struct nes_cm_event *event)
+{
+ atomic_inc(&event->cm_node->cm_core->events_posted);
+ add_ref_cm_node(event->cm_node);
+ event->cm_info.cm_id->add_ref(event->cm_info.cm_id);
+ INIT_WORK(&event->event_work, nes_cm_event_handler);
+ nes_debug(NES_DBG_CM, "queue_work, event=%p\n", event);
+
+ queue_work(event->cm_node->cm_core->event_wq, &event->event_work);
+
+ nes_debug(NES_DBG_CM, "Exit\n");
+ return 0;
+}
+
+
+/**
+ * nes_cm_event_handler
+ * worker function to handle cm events
+ * will free instance of nes_cm_event
+ */
+static void nes_cm_event_handler(struct work_struct *work)
+{
+ struct nes_cm_event *event = container_of(work, struct nes_cm_event, event_work);
+ struct nes_cm_core *cm_core;
+
+ if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core)) {
+ return;
+ }
+ cm_core = event->cm_node->cm_core;
+ nes_debug(NES_DBG_CM, "event=%p, event->type=%u, events posted=%u\n",
+ event, event->type, atomic_read(&cm_core->events_posted));
+
+ switch (event->type) {
+ case NES_CM_EVENT_MPA_REQ:
+ cm_event_mpa_req(event);
+ nes_debug(NES_DBG_CM, "CM Event: MPA REQUEST\n");
+ break;
+ case NES_CM_EVENT_RESET:
+ nes_debug(NES_DBG_CM, "CM Event: RESET\n");
+ cm_event_reset(event);
+ break;
+ case NES_CM_EVENT_CONNECTED:
+ if ((!event->cm_node->cm_id) ||
+ (event->cm_node->state != NES_CM_STATE_TSA)) {
+ break;
+ }
+ cm_event_connected(event);
+ nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n");
+ break;
+ case NES_CM_EVENT_ABORTED:
+ if ((!event->cm_node->cm_id) || (event->cm_node->state == NES_CM_STATE_TSA)) {
+ break;
+ }
+ cm_event_connect_error(event);
+ nes_debug(NES_DBG_CM, "CM Event: ABORTED\n");
+ break;
+ case NES_CM_EVENT_DROPPED_PKT:
+ nes_debug(NES_DBG_CM, "CM Event: DROPPED PKT\n");
+ break;
+ default:
+ nes_debug(NES_DBG_CM, "CM Event: UNKNOWN EVENT TYPE\n");
+ break;
+ }
+
+ atomic_dec(&cm_core->events_posted);
+ event->cm_info.cm_id->rem_ref(event->cm_info.cm_id);
+ rem_ref_cm_node(cm_core, event->cm_node);
+ kfree(event);
+
+ return;
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef NES_CM_H
+#define NES_CM_H
+
+#define QUEUE_EVENTS
+
+#define NES_MANAGE_APBVT_DEL 0
+#define NES_MANAGE_APBVT_ADD 1
+
+/* IETF MPA -- defines, enums, structs */
+#define IEFT_MPA_KEY_REQ "MPA ID Req Frame"
+#define IEFT_MPA_KEY_REP "MPA ID Rep Frame"
+#define IETF_MPA_KEY_SIZE 16
+#define IETF_MPA_VERSION 1
+
+enum ietf_mpa_flags {
+ IETF_MPA_FLAGS_MARKERS = 0x80, /* receive Markers */
+ IETF_MPA_FLAGS_CRC = 0x40, /* receive Markers */
+ IETF_MPA_FLAGS_REJECT = 0x20, /* Reject */
+};
+
+struct ietf_mpa_frame {
+ u8 key[IETF_MPA_KEY_SIZE];
+ u8 flags;
+ u8 rev;
+ __be16 priv_data_len;
+ u8 priv_data[0];
+};
+
+#define ietf_mpa_req_resp_frame ietf_mpa_frame
+
+struct nes_v4_quad {
+ u32 rsvd0;
+ __le32 DstIpAdrIndex; /* Only most significant 5 bits are valid */
+ __be32 SrcIpadr;
+ __be16 TcpPorts[2]; /* src is low, dest is high */
+};
+
+struct nes_cm_node;
+enum nes_timer_type {
+ NES_TIMER_TYPE_SEND,
+ NES_TIMER_TYPE_RECV,
+ NES_TIMER_NODE_CLEANUP,
+ NES_TIMER_TYPE_CLOSE,
+};
+
+#define MAX_NES_IFS 4
+
+#define SET_ACK 1
+#define SET_SYN 2
+#define SET_FIN 4
+#define SET_RST 8
+
+struct option_base {
+ u8 optionnum;
+ u8 length;
+};
+
+enum option_numbers {
+ OPTION_NUMBER_END,
+ OPTION_NUMBER_NONE,
+ OPTION_NUMBER_MSS,
+ OPTION_NUMBER_WINDOW_SCALE,
+ OPTION_NUMBER_SACK_PERM,
+ OPTION_NUMBER_SACK,
+ OPTION_NUMBER_WRITE0 = 0xbc
+};
+
+struct option_mss {
+ u8 optionnum;
+ u8 length;
+ __be16 mss;
+};
+
+struct option_windowscale {
+ u8 optionnum;
+ u8 length;
+ u8 shiftcount;
+};
+
+union all_known_options {
+ char as_end;
+ struct option_base as_base;
+ struct option_mss as_mss;
+ struct option_windowscale as_windowscale;
+};
+
+struct nes_timer_entry {
+ struct list_head list;
+ unsigned long timetosend; /* jiffies */
+ struct sk_buff *skb;
+ u32 type;
+ u32 retrycount;
+ u32 retranscount;
+ u32 context;
+ u32 seq_num;
+ u32 send_retrans;
+ int close_when_complete;
+ struct net_device *netdev;
+};
+
+#define NES_DEFAULT_RETRYS 64
+#define NES_DEFAULT_RETRANS 8
+#ifdef CONFIG_INFINIBAND_NES_DEBUG
+#define NES_RETRY_TIMEOUT (1000*HZ/1000)
+#else
+#define NES_RETRY_TIMEOUT (3000*HZ/1000)
+#endif
+#define NES_SHORT_TIME (10)
+#define NES_LONG_TIME (2000*HZ/1000)
+
+#define NES_CM_HASHTABLE_SIZE 1024
+#define NES_CM_TCP_TIMER_INTERVAL 3000
+#define NES_CM_DEFAULT_MTU 1540
+#define NES_CM_DEFAULT_FRAME_CNT 10
+#define NES_CM_THREAD_STACK_SIZE 256
+#define NES_CM_DEFAULT_RCV_WND 64240 // before we know that window scaling is allowed
+#define NES_CM_DEFAULT_RCV_WND_SCALED 256960 // after we know that window scaling is allowed
+#define NES_CM_DEFAULT_RCV_WND_SCALE 2
+#define NES_CM_DEFAULT_FREE_PKTS 0x000A
+#define NES_CM_FREE_PKT_LO_WATERMARK 2
+
+#define NES_CM_DEFAULT_MSS 536
+
+#define NES_CM_DEF_SEQ 0x159bf75f
+#define NES_CM_DEF_LOCAL_ID 0x3b47
+
+#define NES_CM_DEF_SEQ2 0x18ed5740
+#define NES_CM_DEF_LOCAL_ID2 0xb807
+
+typedef u32 nes_addr_t;
+
+#define nes_cm_tsa_context nes_qp_context
+
+struct nes_qp;
+
+/* cm node transition states */
+enum nes_cm_node_state {
+ NES_CM_STATE_UNKNOWN,
+ NES_CM_STATE_INITED,
+ NES_CM_STATE_LISTENING,
+ NES_CM_STATE_SYN_RCVD,
+ NES_CM_STATE_SYN_SENT,
+ NES_CM_STATE_ONE_SIDE_ESTABLISHED,
+ NES_CM_STATE_ESTABLISHED,
+ NES_CM_STATE_ACCEPTING,
+ NES_CM_STATE_MPAREQ_SENT,
+ NES_CM_STATE_TSA,
+ NES_CM_STATE_FIN_WAIT1,
+ NES_CM_STATE_FIN_WAIT2,
+ NES_CM_STATE_CLOSE_WAIT,
+ NES_CM_STATE_TIME_WAIT,
+ NES_CM_STATE_LAST_ACK,
+ NES_CM_STATE_CLOSING,
+ NES_CM_STATE_CLOSED
+};
+
+/* type of nes connection */
+enum nes_cm_conn_type {
+ NES_CM_IWARP_CONN_TYPE,
+};
+
+/* CM context params */
+struct nes_cm_tcp_context {
+ u8 client;
+
+ u32 loc_seq_num;
+ u32 loc_ack_num;
+ u32 rem_ack_num;
+ u32 rcv_nxt;
+
+ u32 loc_id;
+ u32 rem_id;
+
+ u32 snd_wnd;
+ u32 max_snd_wnd;
+
+ u32 rcv_wnd;
+ u32 mss;
+ u8 snd_wscale;
+ u8 rcv_wscale;
+
+ struct nes_cm_tsa_context tsa_cntxt;
+ struct timeval sent_ts;
+};
+
+
+enum nes_cm_listener_state {
+ NES_CM_LISTENER_PASSIVE_STATE=1,
+ NES_CM_LISTENER_ACTIVE_STATE=2,
+ NES_CM_LISTENER_EITHER_STATE=3
+};
+
+struct nes_cm_listener {
+ struct list_head list;
+ u64 session_id;
+ struct nes_cm_core *cm_core;
+ u8 loc_mac[ETH_ALEN];
+ nes_addr_t loc_addr;
+ u16 loc_port;
+ struct iw_cm_id *cm_id;
+ enum nes_cm_conn_type conn_type;
+ atomic_t ref_count;
+ struct nes_vnic *nesvnic;
+ atomic_t pend_accepts_cnt;
+ int backlog;
+ enum nes_cm_listener_state listener_state;
+ u32 reused_node;
+};
+
+/* per connection node and node state information */
+struct nes_cm_node {
+ u64 session_id;
+ u32 hashkey;
+
+ nes_addr_t loc_addr, rem_addr;
+ u16 loc_port, rem_port;
+
+ u8 loc_mac[ETH_ALEN];
+ u8 rem_mac[ETH_ALEN];
+
+ enum nes_cm_node_state state;
+ struct nes_cm_tcp_context tcp_cntxt;
+ struct nes_cm_core *cm_core;
+ struct sk_buff_head resend_list;
+ atomic_t ref_count;
+ struct net_device *netdev;
+
+ struct nes_cm_node *loopbackpartner;
+ struct list_head retrans_list;
+ spinlock_t retrans_list_lock;
+ struct list_head recv_list;
+ spinlock_t recv_list_lock;
+
+ int send_write0;
+ union {
+ struct ietf_mpa_frame mpa_frame;
+ u8 mpa_frame_buf[NES_CM_DEFAULT_MTU];
+ };
+ u16 mpa_frame_size;
+ struct iw_cm_id *cm_id;
+ struct list_head list;
+ int accelerated;
+ struct nes_cm_listener *listener;
+ enum nes_cm_conn_type conn_type;
+ struct nes_vnic *nesvnic;
+ int apbvt_set;
+ int accept_pend;
+};
+
+/* structure for client or CM to fill when making CM api calls. */
+/* - only need to set relevant data, based on op. */
+struct nes_cm_info {
+ union {
+ struct iw_cm_id *cm_id;
+ struct net_device *netdev;
+ };
+
+ u16 loc_port;
+ u16 rem_port;
+ nes_addr_t loc_addr;
+ nes_addr_t rem_addr;
+
+ enum nes_cm_conn_type conn_type;
+ int backlog;
+};
+
+/* CM event codes */
+enum nes_cm_event_type {
+ NES_CM_EVENT_UNKNOWN,
+ NES_CM_EVENT_ESTABLISHED,
+ NES_CM_EVENT_MPA_REQ,
+ NES_CM_EVENT_MPA_CONNECT,
+ NES_CM_EVENT_MPA_ACCEPT,
+ NES_CM_EVENT_MPA_ESTABLISHED,
+ NES_CM_EVENT_CONNECTED,
+ NES_CM_EVENT_CLOSED,
+ NES_CM_EVENT_RESET,
+ NES_CM_EVENT_DROPPED_PKT,
+ NES_CM_EVENT_CLOSE_IMMED,
+ NES_CM_EVENT_CLOSE_HARD,
+ NES_CM_EVENT_CLOSE_CLEAN,
+ NES_CM_EVENT_ABORTED,
+ NES_CM_EVENT_SEND_FIRST
+};
+
+/* event to post to CM event handler */
+struct nes_cm_event {
+ enum nes_cm_event_type type;
+
+ struct nes_cm_info cm_info;
+ struct work_struct event_work;
+ struct nes_cm_node *cm_node;
+};
+
+struct nes_cm_core {
+ enum nes_cm_node_state state;
+ atomic_t session_id;
+
+ atomic_t listen_node_cnt;
+ struct nes_cm_node listen_list;
+ spinlock_t listen_list_lock;
+
+ u32 mtu;
+ u32 free_tx_pkt_max;
+ u32 rx_pkt_posted;
+ struct sk_buff_head tx_free_list;
+ atomic_t ht_node_cnt;
+ struct list_head connected_nodes;
+ /* struct list_head hashtable[NES_CM_HASHTABLE_SIZE]; */
+ spinlock_t ht_lock;
+
+ struct timer_list tcp_timer;
+
+ struct nes_cm_ops *api;
+
+ int (*post_event)(struct nes_cm_event *event);
+ atomic_t events_posted;
+ struct workqueue_struct *event_wq;
+ struct workqueue_struct *disconn_wq;
+
+ atomic_t node_cnt;
+ u64 aborted_connects;
+ u32 options;
+
+ struct nes_cm_node *current_listen_node;
+};
+
+
+#define NES_CM_SET_PKT_SIZE (1 << 1)
+#define NES_CM_SET_FREE_PKT_Q_SIZE (1 << 2)
+
+/* CM ops/API for client interface */
+struct nes_cm_ops {
+ int (*accelerated)(struct nes_cm_core *, struct nes_cm_node *);
+ struct nes_cm_listener * (*listen)(struct nes_cm_core *, struct nes_vnic *,
+ struct nes_cm_info *);
+ int (*stop_listener)(struct nes_cm_core *, struct nes_cm_listener *);
+ struct nes_cm_node * (*connect)(struct nes_cm_core *,
+ struct nes_vnic *, struct ietf_mpa_frame *,
+ struct nes_cm_info *);
+ int (*close)(struct nes_cm_core *, struct nes_cm_node *);
+ int (*accept)(struct nes_cm_core *, struct ietf_mpa_frame *,
+ struct nes_cm_node *);
+ int (*reject)(struct nes_cm_core *, struct ietf_mpa_frame *,
+ struct nes_cm_node *);
+ int (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
+ struct sk_buff *);
+ int (*destroy_cm_core)(struct nes_cm_core *);
+ int (*get)(struct nes_cm_core *);
+ int (*set)(struct nes_cm_core *, u32, u32);
+};
+
+
+int send_mpa_request(struct nes_cm_node *);
+struct sk_buff *form_cm_frame(struct sk_buff *, struct nes_cm_node *,
+ void *, u32, void *, u32, u8);
+int schedule_nes_timer(struct nes_cm_node *, struct sk_buff *,
+ enum nes_timer_type, int, int);
+void nes_cm_timer_tick(unsigned long);
+int send_syn(struct nes_cm_node *, u32);
+int send_reset(struct nes_cm_node *);
+int send_ack(struct nes_cm_node *);
+int send_fin(struct nes_cm_node *, struct sk_buff *);
+struct sk_buff *get_free_pkt(struct nes_cm_node *);
+int process_packet(struct nes_cm_node *, struct sk_buff *, struct nes_cm_core *);
+
+struct nes_cm_node * mini_cm_connect(struct nes_cm_core *,
+ struct nes_vnic *, struct ietf_mpa_frame *, struct nes_cm_info *);
+int mini_cm_accept(struct nes_cm_core *, struct ietf_mpa_frame *, struct nes_cm_node *);
+int mini_cm_reject(struct nes_cm_core *, struct ietf_mpa_frame *, struct nes_cm_node *);
+int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *);
+int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *, struct sk_buff *);
+struct nes_cm_core *mini_cm_alloc_core(struct nes_cm_info *);
+int mini_cm_dealloc_core(struct nes_cm_core *);
+int mini_cm_get(struct nes_cm_core *);
+int mini_cm_set(struct nes_cm_core *, u32, u32);
+
+int nes_cm_disconn(struct nes_qp *);
+void nes_disconnect_worker(struct work_struct *);
+int nes_cm_disconn_true(struct nes_qp *);
+int nes_disconnect(struct nes_qp *, int);
+
+int nes_accept(struct iw_cm_id *, struct iw_cm_conn_param *);
+int nes_reject(struct iw_cm_id *, const void *, u8);
+int nes_connect(struct iw_cm_id *, struct iw_cm_conn_param *);
+int nes_create_listen(struct iw_cm_id *, int);
+int nes_destroy_listen(struct iw_cm_id *);
+
+int nes_cm_recv(struct sk_buff *, struct net_device *);
+int nes_cm_start(void);
+int nes_cm_stop(void);
+
+/* CM event handler functions */
+void cm_event_connected(struct nes_cm_event *);
+void cm_event_connect_error(struct nes_cm_event *);
+void cm_event_reset(struct nes_cm_event *);
+void cm_event_mpa_req(struct nes_cm_event *);
+int nes_cm_post_event(struct nes_cm_event *);
+
+#endif /* NES_CM_H */
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef NES_CONTEXT_H
+#define NES_CONTEXT_H
+
+struct nes_qp_context {
+ __le32 misc;
+ __le32 cqs;
+ __le32 sq_addr_low;
+ __le32 sq_addr_high;
+ __le32 rq_addr_low;
+ __le32 rq_addr_high;
+ __le32 misc2;
+ __le16 tcpPorts[2];
+ __le32 ip0;
+ __le32 ip1;
+ __le32 ip2;
+ __le32 ip3;
+ __le32 mss;
+ __le32 arp_index_vlan;
+ __le32 tcp_state_flow_label;
+ __le32 pd_index_wscale;
+ __le32 keepalive;
+ u32 ts_recent;
+ u32 ts_age;
+ __le32 snd_nxt;
+ __le32 snd_wnd;
+ __le32 rcv_nxt;
+ __le32 rcv_wnd;
+ __le32 snd_max;
+ __le32 snd_una;
+ u32 srtt;
+ __le32 rttvar;
+ __le32 ssthresh;
+ __le32 cwnd;
+ __le32 snd_wl1;
+ __le32 snd_wl2;
+ __le32 max_snd_wnd;
+ __le32 ts_val_delta;
+ u32 retransmit;
+ u32 probe_cnt;
+ u32 hte_index;
+ __le32 q2_addr_low;
+ __le32 q2_addr_high;
+ __le32 ird_index;
+ u32 Rsvd3;
+ __le32 ird_ord_sizes;
+ u32 mrkr_offset;
+ __le32 aeq_token_low;
+ __le32 aeq_token_high;
+};
+
+/* QP Context Misc Field */
+
+#define NES_QPCONTEXT_MISC_IWARP_VER_MASK 0x00000003
+#define NES_QPCONTEXT_MISC_IWARP_VER_SHIFT 0
+#define NES_QPCONTEXT_MISC_EFB_SIZE_MASK 0x000000C0
+#define NES_QPCONTEXT_MISC_EFB_SIZE_SHIFT 6
+#define NES_QPCONTEXT_MISC_RQ_SIZE_MASK 0x00000300
+#define NES_QPCONTEXT_MISC_RQ_SIZE_SHIFT 8
+#define NES_QPCONTEXT_MISC_SQ_SIZE_MASK 0x00000c00
+#define NES_QPCONTEXT_MISC_SQ_SIZE_SHIFT 10
+#define NES_QPCONTEXT_MISC_PCI_FCN_MASK 0x00007000
+#define NES_QPCONTEXT_MISC_PCI_FCN_SHIFT 12
+#define NES_QPCONTEXT_MISC_DUP_ACKS_MASK 0x00070000
+#define NES_QPCONTEXT_MISC_DUP_ACKS_SHIFT 16
+
+enum nes_qp_context_misc_bits {
+ NES_QPCONTEXT_MISC_RX_WQE_SIZE = 0x00000004,
+ NES_QPCONTEXT_MISC_IPV4 = 0x00000008,
+ NES_QPCONTEXT_MISC_DO_NOT_FRAG = 0x00000010,
+ NES_QPCONTEXT_MISC_INSERT_VLAN = 0x00000020,
+ NES_QPCONTEXT_MISC_DROS = 0x00008000,
+ NES_QPCONTEXT_MISC_WSCALE = 0x00080000,
+ NES_QPCONTEXT_MISC_KEEPALIVE = 0x00100000,
+ NES_QPCONTEXT_MISC_TIMESTAMP = 0x00200000,
+ NES_QPCONTEXT_MISC_SACK = 0x00400000,
+ NES_QPCONTEXT_MISC_RDMA_WRITE_EN = 0x00800000,
+ NES_QPCONTEXT_MISC_RDMA_READ_EN = 0x01000000,
+ NES_QPCONTEXT_MISC_WBIND_EN = 0x10000000,
+ NES_QPCONTEXT_MISC_FAST_REGISTER_EN = 0x20000000,
+ NES_QPCONTEXT_MISC_PRIV_EN = 0x40000000,
+ NES_QPCONTEXT_MISC_NO_NAGLE = 0x80000000
+};
+
+enum nes_qp_acc_wq_sizes {
+ HCONTEXT_TSA_WQ_SIZE_4 = 0,
+ HCONTEXT_TSA_WQ_SIZE_32 = 1,
+ HCONTEXT_TSA_WQ_SIZE_128 = 2,
+ HCONTEXT_TSA_WQ_SIZE_512 = 3
+};
+
+/* QP Context Misc2 Fields */
+#define NES_QPCONTEXT_MISC2_TTL_MASK 0x000000ff
+#define NES_QPCONTEXT_MISC2_TTL_SHIFT 0
+#define NES_QPCONTEXT_MISC2_HOP_LIMIT_MASK 0x000000ff
+#define NES_QPCONTEXT_MISC2_HOP_LIMIT_SHIFT 0
+#define NES_QPCONTEXT_MISC2_LIMIT_MASK 0x00000300
+#define NES_QPCONTEXT_MISC2_LIMIT_SHIFT 8
+#define NES_QPCONTEXT_MISC2_NIC_INDEX_MASK 0x0000fc00
+#define NES_QPCONTEXT_MISC2_NIC_INDEX_SHIFT 10
+#define NES_QPCONTEXT_MISC2_SRC_IP_MASK 0x001f0000
+#define NES_QPCONTEXT_MISC2_SRC_IP_SHIFT 16
+#define NES_QPCONTEXT_MISC2_TOS_MASK 0xff000000
+#define NES_QPCONTEXT_MISC2_TOS_SHIFT 24
+#define NES_QPCONTEXT_MISC2_TRAFFIC_CLASS_MASK 0xff000000
+#define NES_QPCONTEXT_MISC2_TRAFFIC_CLASS_SHIFT 24
+
+/* QP Context Tcp State/Flow Label Fields */
+#define NES_QPCONTEXT_TCPFLOW_FLOW_LABEL_MASK 0x000fffff
+#define NES_QPCONTEXT_TCPFLOW_FLOW_LABEL_SHIFT 0
+#define NES_QPCONTEXT_TCPFLOW_TCP_STATE_MASK 0xf0000000
+#define NES_QPCONTEXT_TCPFLOW_TCP_STATE_SHIFT 28
+
+enum nes_qp_tcp_state {
+ NES_QPCONTEXT_TCPSTATE_CLOSED = 1,
+ NES_QPCONTEXT_TCPSTATE_EST = 5,
+ NES_QPCONTEXT_TCPSTATE_TIME_WAIT = 11,
+};
+
+/* QP Context PD Index/wscale Fields */
+#define NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_MASK 0x0000000f
+#define NES_QPCONTEXT_PDWSCALE_RCV_WSCALE_SHIFT 0
+#define NES_QPCONTEXT_PDWSCALE_SND_WSCALE_MASK 0x00000f00
+#define NES_QPCONTEXT_PDWSCALE_SND_WSCALE_SHIFT 8
+#define NES_QPCONTEXT_PDWSCALE_PDINDEX_MASK 0xffff0000
+#define NES_QPCONTEXT_PDWSCALE_PDINDEX_SHIFT 16
+
+/* QP Context Keepalive Fields */
+#define NES_QPCONTEXT_KEEPALIVE_DELTA_MASK 0x0000ffff
+#define NES_QPCONTEXT_KEEPALIVE_DELTA_SHIFT 0
+#define NES_QPCONTEXT_KEEPALIVE_PROBE_CNT_MASK 0x00ff0000
+#define NES_QPCONTEXT_KEEPALIVE_PROBE_CNT_SHIFT 16
+#define NES_QPCONTEXT_KEEPALIVE_INTV_MASK 0xff000000
+#define NES_QPCONTEXT_KEEPALIVE_INTV_SHIFT 24
+
+/* QP Context ORD/IRD Fields */
+#define NES_QPCONTEXT_ORDIRD_ORDSIZE_MASK 0x0000007f
+#define NES_QPCONTEXT_ORDIRD_ORDSIZE_SHIFT 0
+#define NES_QPCONTEXT_ORDIRD_IRDSIZE_MASK 0x00030000
+#define NES_QPCONTEXT_ORDIRD_IRDSIZE_SHIFT 16
+#define NES_QPCONTEXT_ORDIRD_IWARP_MODE_MASK 0x30000000
+#define NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT 28
+
+enum nes_ord_ird_bits {
+ NES_QPCONTEXT_ORDIRD_WRPDU = 0x02000000,
+ NES_QPCONTEXT_ORDIRD_LSMM_PRESENT = 0x04000000,
+ NES_QPCONTEXT_ORDIRD_ALSMM = 0x08000000,
+ NES_QPCONTEXT_ORDIRD_AAH = 0x40000000,
+ NES_QPCONTEXT_ORDIRD_RNMC = 0x80000000
+};
+
+enum nes_iwarp_qp_state {
+ NES_QPCONTEXT_IWARP_STATE_NONEXIST = 0,
+ NES_QPCONTEXT_IWARP_STATE_IDLE = 1,
+ NES_QPCONTEXT_IWARP_STATE_RTS = 2,
+ NES_QPCONTEXT_IWARP_STATE_CLOSING = 3,
+ NES_QPCONTEXT_IWARP_STATE_TERMINATE = 5,
+ NES_QPCONTEXT_IWARP_STATE_ERROR = 6
+};
+
+
+#endif /* NES_CONTEXT_H */
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_vlan.h>
+
+#include "nes.h"
+
+u32 crit_err_count = 0;
+u32 int_mod_timer_init;
+u32 int_mod_cq_depth_256;
+u32 int_mod_cq_depth_128;
+u32 int_mod_cq_depth_32;
+u32 int_mod_cq_depth_24;
+u32 int_mod_cq_depth_16;
+u32 int_mod_cq_depth_4;
+u32 int_mod_cq_depth_1;
+
+#include "nes_cm.h"
+
+
+#ifdef CONFIG_INFINIBAND_NES_DEBUG
+static unsigned char *nes_iwarp_state_str[] = {
+ "Non-Existant",
+ "Idle",
+ "RTS",
+ "Closing",
+ "RSVD1",
+ "Terminate",
+ "Error",
+ "RSVD2",
+};
+
+static unsigned char *nes_tcp_state_str[] = {
+ "Non-Existant",
+ "Closed",
+ "Listen",
+ "SYN Sent",
+ "SYN Rcvd",
+ "Established",
+ "Close Wait",
+ "FIN Wait 1",
+ "Closing",
+ "Last Ack",
+ "FIN Wait 2",
+ "Time Wait",
+ "RSVD1",
+ "RSVD2",
+ "RSVD3",
+ "RSVD4",
+};
+#endif
+
+
+/**
+ * nes_nic_init_timer_defaults
+ */
+void nes_nic_init_timer_defaults(struct nes_device *nesdev, u8 jumbomode)
+{
+ unsigned long flags;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_tune_timer *shared_timer = &nesadapter->tune_timer;
+
+ spin_lock_irqsave(&nesadapter->periodic_timer_lock, flags);
+
+ shared_timer->timer_in_use_min = NES_NIC_FAST_TIMER_LOW;
+ shared_timer->timer_in_use_max = NES_NIC_FAST_TIMER_HIGH;
+ if (jumbomode) {
+ shared_timer->threshold_low = DEFAULT_JUMBO_NES_QL_LOW;
+ shared_timer->threshold_target = DEFAULT_JUMBO_NES_QL_TARGET;
+ shared_timer->threshold_high = DEFAULT_JUMBO_NES_QL_HIGH;
+ } else {
+ shared_timer->threshold_low = DEFAULT_NES_QL_LOW;
+ shared_timer->threshold_target = DEFAULT_NES_QL_TARGET;
+ shared_timer->threshold_high = DEFAULT_NES_QL_HIGH;
+ }
+
+ /* todo use netdev->mtu to set thresholds */
+ spin_unlock_irqrestore(&nesadapter->periodic_timer_lock, flags);
+}
+
+
+/**
+ * nes_nic_init_timer
+ */
+static void nes_nic_init_timer(struct nes_device *nesdev)
+{
+ unsigned long flags;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_tune_timer *shared_timer = &nesadapter->tune_timer;
+
+ spin_lock_irqsave(&nesadapter->periodic_timer_lock, flags);
+
+ if (shared_timer->timer_in_use_old == 0) {
+ nesdev->deepcq_count = 0;
+ shared_timer->timer_direction_upward = 0;
+ shared_timer->timer_direction_downward = 0;
+ shared_timer->timer_in_use = NES_NIC_FAST_TIMER;
+ shared_timer->timer_in_use_old = 0;
+
+ }
+ if (shared_timer->timer_in_use != shared_timer->timer_in_use_old) {
+ shared_timer->timer_in_use_old = shared_timer->timer_in_use;
+ nes_write32(nesdev->regs+NES_PERIODIC_CONTROL,
+ 0x80000000 | ((u32)(shared_timer->timer_in_use*8)));
+ }
+ /* todo use netdev->mtu to set thresholds */
+ spin_unlock_irqrestore(&nesadapter->periodic_timer_lock, flags);
+}
+
+
+/**
+ * nes_nic_tune_timer
+ */
+static void nes_nic_tune_timer(struct nes_device *nesdev)
+{
+ unsigned long flags;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_tune_timer *shared_timer = &nesadapter->tune_timer;
+ u16 cq_count = nesdev->currcq_count;
+
+ spin_lock_irqsave(&nesadapter->periodic_timer_lock, flags);
+
+ if (shared_timer->cq_count_old < cq_count) {
+ if (cq_count > shared_timer->threshold_low)
+ shared_timer->cq_direction_downward=0;
+ }
+ if (shared_timer->cq_count_old >= cq_count)
+ shared_timer->cq_direction_downward++;
+ shared_timer->cq_count_old = cq_count;
+ if (shared_timer->cq_direction_downward > NES_NIC_CQ_DOWNWARD_TREND) {
+ if (cq_count <= shared_timer->threshold_low) {
+ shared_timer->threshold_low = shared_timer->threshold_low/2;
+ shared_timer->cq_direction_downward=0;
+ nesdev->currcq_count = 0;
+ spin_unlock_irqrestore(&nesadapter->periodic_timer_lock, flags);
+ return;
+ }
+ }
+
+ if (cq_count > 1) {
+ nesdev->deepcq_count += cq_count;
+ if (cq_count <= shared_timer->threshold_low) { /* increase timer gently */
+ shared_timer->timer_direction_upward++;
+ shared_timer->timer_direction_downward = 0;
+ } else if (cq_count <= shared_timer->threshold_target) { /* balanced */
+ shared_timer->timer_direction_upward = 0;
+ shared_timer->timer_direction_downward = 0;
+ } else if (cq_count <= shared_timer->threshold_high) { /* decrease timer gently */
+ shared_timer->timer_direction_downward++;
+ shared_timer->timer_direction_upward = 0;
+ } else if (cq_count <= (shared_timer->threshold_high) * 2) {
+ shared_timer->timer_in_use -= 2;
+ shared_timer->timer_direction_upward = 0;
+ shared_timer->timer_direction_downward++;
+ } else {
+ shared_timer->timer_in_use -= 4;
+ shared_timer->timer_direction_upward = 0;
+ shared_timer->timer_direction_downward++;
+ }
+
+ if (shared_timer->timer_direction_upward > 3 ) { /* using history */
+ shared_timer->timer_in_use += 3;
+ shared_timer->timer_direction_upward = 0;
+ shared_timer->timer_direction_downward = 0;
+ }
+ if (shared_timer->timer_direction_downward > 5) { /* using history */
+ shared_timer->timer_in_use -= 4 ;
+ shared_timer->timer_direction_downward = 0;
+ shared_timer->timer_direction_upward = 0;
+ }
+ }
+
+ /* boundary checking */
+ if (shared_timer->timer_in_use > NES_NIC_FAST_TIMER_HIGH)
+ shared_timer->timer_in_use = NES_NIC_FAST_TIMER_HIGH;
+ else if (shared_timer->timer_in_use < NES_NIC_FAST_TIMER_LOW) {
+ shared_timer->timer_in_use = NES_NIC_FAST_TIMER_LOW;
+ }
+
+ nesdev->currcq_count = 0;
+
+ spin_unlock_irqrestore(&nesadapter->periodic_timer_lock, flags);
+}
+
+
+/**
+ * nes_init_adapter - initialize adapter
+ */
+struct nes_adapter *nes_init_adapter(struct nes_device *nesdev, u8 hw_rev) {
+ struct nes_adapter *nesadapter = NULL;
+ unsigned long num_pds;
+ u32 u32temp;
+ u32 port_count;
+ u16 max_rq_wrs;
+ u16 max_sq_wrs;
+ u32 max_mr;
+ u32 max_256pbl;
+ u32 max_4kpbl;
+ u32 max_qp;
+ u32 max_irrq;
+ u32 max_cq;
+ u32 hte_index_mask;
+ u32 adapter_size;
+ u32 arp_table_size;
+ u16 vendor_id;
+ u8 OneG_Mode;
+ u8 func_index;
+
+ /* search the list of existing adapters */
+ list_for_each_entry(nesadapter, &nes_adapter_list, list) {
+ nes_debug(NES_DBG_INIT, "Searching Adapter list for PCI devfn = 0x%X,"
+ " adapter PCI slot/bus = %u/%u, pci devices PCI slot/bus = %u/%u, .\n",
+ nesdev->pcidev->devfn,
+ PCI_SLOT(nesadapter->devfn),
+ nesadapter->bus_number,
+ PCI_SLOT(nesdev->pcidev->devfn),
+ nesdev->pcidev->bus->number );
+ if ((PCI_SLOT(nesadapter->devfn) == PCI_SLOT(nesdev->pcidev->devfn)) &&
+ (nesadapter->bus_number == nesdev->pcidev->bus->number)) {
+ nesadapter->ref_count++;
+ return nesadapter;
+ }
+ }
+
+ /* no adapter found */
+ num_pds = pci_resource_len(nesdev->pcidev, BAR_1) >> PAGE_SHIFT;
+ if ((hw_rev != NE020_REV) && (hw_rev != NE020_REV1)) {
+ nes_debug(NES_DBG_INIT, "NE020 driver detected unknown hardware revision 0x%x\n",
+ hw_rev);
+ return NULL;
+ }
+
+ nes_debug(NES_DBG_INIT, "Determine Soft Reset, QP_control=0x%x, CPU0=0x%x, CPU1=0x%x, CPU2=0x%x\n",
+ nes_read_indexed(nesdev, NES_IDX_QP_CONTROL + PCI_FUNC(nesdev->pcidev->devfn) * 8),
+ nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS),
+ nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS + 4),
+ nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS + 8));
+
+ nes_debug(NES_DBG_INIT, "Reset and init NE020\n");
+
+
+ if ((port_count = nes_reset_adapter_ne020(nesdev, &OneG_Mode)) == 0)
+ return NULL;
+ if (nes_init_serdes(nesdev, hw_rev, port_count, OneG_Mode))
+ return NULL;
+ nes_init_csr_ne020(nesdev, hw_rev, port_count);
+
+ max_qp = nes_read_indexed(nesdev, NES_IDX_QP_CTX_SIZE);
+ nes_debug(NES_DBG_INIT, "QP_CTX_SIZE=%u\n", max_qp);
+
+ u32temp = nes_read_indexed(nesdev, NES_IDX_QUAD_HASH_TABLE_SIZE);
+ if (max_qp > ((u32)1 << (u32temp & 0x001f))) {
+ nes_debug(NES_DBG_INIT, "Reducing Max QPs to %u due to hash table size = 0x%08X\n",
+ max_qp, u32temp);
+ max_qp = (u32)1 << (u32temp & 0x001f);
+ }
+
+ hte_index_mask = ((u32)1 << ((u32temp & 0x001f)+1))-1;
+ nes_debug(NES_DBG_INIT, "Max QP = %u, hte_index_mask = 0x%08X.\n",
+ max_qp, hte_index_mask);
+
+ u32temp = nes_read_indexed(nesdev, NES_IDX_IRRQ_COUNT);
+
+ max_irrq = 1 << (u32temp & 0x001f);
+
+ if (max_qp > max_irrq) {
+ max_qp = max_irrq;
+ nes_debug(NES_DBG_INIT, "Reducing Max QPs to %u due to Available Q1s.\n",
+ max_qp);
+ }
+
+ /* there should be no reason to allocate more pds than qps */
+ if (num_pds > max_qp)
+ num_pds = max_qp;
+
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MRT_SIZE);
+ max_mr = (u32)8192 << (u32temp & 0x7);
+
+ u32temp = nes_read_indexed(nesdev, NES_IDX_PBL_REGION_SIZE);
+ max_256pbl = (u32)1 << (u32temp & 0x0000001f);
+ max_4kpbl = (u32)1 << ((u32temp >> 16) & 0x0000001f);
+ max_cq = nes_read_indexed(nesdev, NES_IDX_CQ_CTX_SIZE);
+
+ u32temp = nes_read_indexed(nesdev, NES_IDX_ARP_CACHE_SIZE);
+ arp_table_size = 1 << u32temp;
+
+ adapter_size = (sizeof(struct nes_adapter) +
+ (sizeof(unsigned long)-1)) & (~(sizeof(unsigned long)-1));
+ adapter_size += sizeof(unsigned long) * BITS_TO_LONGS(max_qp);
+ adapter_size += sizeof(unsigned long) * BITS_TO_LONGS(max_mr);
+ adapter_size += sizeof(unsigned long) * BITS_TO_LONGS(max_cq);
+ adapter_size += sizeof(unsigned long) * BITS_TO_LONGS(num_pds);
+ adapter_size += sizeof(unsigned long) * BITS_TO_LONGS(arp_table_size);
+ adapter_size += sizeof(struct nes_qp **) * max_qp;
+
+ /* allocate a new adapter struct */
+ nesadapter = kzalloc(adapter_size, GFP_KERNEL);
+ if (nesadapter == NULL) {
+ return NULL;
+ }
+
+ nes_debug(NES_DBG_INIT, "Allocating new nesadapter @ %p, size = %u (actual size = %u).\n",
+ nesadapter, (u32)sizeof(struct nes_adapter), adapter_size);
+
+ /* populate the new nesadapter */
+ nesadapter->devfn = nesdev->pcidev->devfn;
+ nesadapter->bus_number = nesdev->pcidev->bus->number;
+ nesadapter->ref_count = 1;
+ nesadapter->timer_int_req = 0xffff0000;
+ nesadapter->OneG_Mode = OneG_Mode;
+ nesadapter->doorbell_start = nesdev->doorbell_region;
+
+ /* nesadapter->tick_delta = clk_divisor; */
+ nesadapter->hw_rev = hw_rev;
+ nesadapter->port_count = port_count;
+
+ nesadapter->max_qp = max_qp;
+ nesadapter->hte_index_mask = hte_index_mask;
+ nesadapter->max_irrq = max_irrq;
+ nesadapter->max_mr = max_mr;
+ nesadapter->max_256pbl = max_256pbl - 1;
+ nesadapter->max_4kpbl = max_4kpbl - 1;
+ nesadapter->max_cq = max_cq;
+ nesadapter->free_256pbl = max_256pbl - 1;
+ nesadapter->free_4kpbl = max_4kpbl - 1;
+ nesadapter->max_pd = num_pds;
+ nesadapter->arp_table_size = arp_table_size;
+
+ nesadapter->et_pkt_rate_low = NES_TIMER_ENABLE_LIMIT;
+ if (nes_drv_opt & NES_DRV_OPT_DISABLE_INT_MOD) {
+ nesadapter->et_use_adaptive_rx_coalesce = 0;
+ nesadapter->timer_int_limit = NES_TIMER_INT_LIMIT;
+ nesadapter->et_rx_coalesce_usecs_irq = interrupt_mod_interval;
+ } else {
+ nesadapter->et_use_adaptive_rx_coalesce = 1;
+ nesadapter->timer_int_limit = NES_TIMER_INT_LIMIT_DYNAMIC;
+ nesadapter->et_rx_coalesce_usecs_irq = 0;
+ printk(PFX "%s: Using Adaptive Interrupt Moderation\n", __FUNCTION__);
+ }
+ /* Setup and enable the periodic timer */
+ if (nesadapter->et_rx_coalesce_usecs_irq)
+ nes_write32(nesdev->regs+NES_PERIODIC_CONTROL, 0x80000000 |
+ ((u32)(nesadapter->et_rx_coalesce_usecs_irq * 8)));
+ else
+ nes_write32(nesdev->regs+NES_PERIODIC_CONTROL, 0x00000000);
+
+ nesadapter->base_pd = 1;
+
+ nesadapter->device_cap_flags =
+ IB_DEVICE_ZERO_STAG | IB_DEVICE_SEND_W_INV | IB_DEVICE_MEM_WINDOW;
+
+ nesadapter->allocated_qps = (unsigned long *)&(((unsigned char *)nesadapter)
+ [(sizeof(struct nes_adapter)+(sizeof(unsigned long)-1))&(~(sizeof(unsigned long)-1))]);
+ nesadapter->allocated_cqs = &nesadapter->allocated_qps[BITS_TO_LONGS(max_qp)];
+ nesadapter->allocated_mrs = &nesadapter->allocated_cqs[BITS_TO_LONGS(max_cq)];
+ nesadapter->allocated_pds = &nesadapter->allocated_mrs[BITS_TO_LONGS(max_mr)];
+ nesadapter->allocated_arps = &nesadapter->allocated_pds[BITS_TO_LONGS(num_pds)];
+ nesadapter->qp_table = (struct nes_qp **)(&nesadapter->allocated_arps[BITS_TO_LONGS(arp_table_size)]);
+
+
+ /* mark the usual suspect QPs and CQs as in use */
+ for (u32temp = 0; u32temp < NES_FIRST_QPN; u32temp++) {
+ set_bit(u32temp, nesadapter->allocated_qps);
+ set_bit(u32temp, nesadapter->allocated_cqs);
+ }
+
+ for (u32temp = 0; u32temp < 20; u32temp++)
+ set_bit(u32temp, nesadapter->allocated_pds);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_QP_MAX_CFG_SIZES);
+
+ max_rq_wrs = ((u32temp >> 8) & 3);
+ switch (max_rq_wrs) {
+ case 0:
+ max_rq_wrs = 4;
+ break;
+ case 1:
+ max_rq_wrs = 16;
+ break;
+ case 2:
+ max_rq_wrs = 32;
+ break;
+ case 3:
+ max_rq_wrs = 512;
+ break;
+ }
+
+ max_sq_wrs = (u32temp & 3);
+ switch (max_sq_wrs) {
+ case 0:
+ max_sq_wrs = 4;
+ break;
+ case 1:
+ max_sq_wrs = 16;
+ break;
+ case 2:
+ max_sq_wrs = 32;
+ break;
+ case 3:
+ max_sq_wrs = 512;
+ break;
+ }
+ nesadapter->max_qp_wr = min(max_rq_wrs, max_sq_wrs);
+ nesadapter->max_irrq_wr = (u32temp >> 16) & 3;
+
+ nesadapter->max_sge = 4;
+ nesadapter->max_cqe = 32767;
+
+ if (nes_read_eeprom_values(nesdev, nesadapter)) {
+ printk(KERN_ERR PFX "Unable to read EEPROM data.\n");
+ kfree(nesadapter);
+ return NULL;
+ }
+
+ u32temp = nes_read_indexed(nesdev, NES_IDX_TCP_TIMER_CONFIG);
+ nes_write_indexed(nesdev, NES_IDX_TCP_TIMER_CONFIG,
+ (u32temp & 0xff000000) | (nesadapter->tcp_timer_core_clk_divisor & 0x00ffffff));
+
+ /* setup port configuration */
+ if (nesadapter->port_count == 1) {
+ u32temp = 0x00000000;
+ if (nes_drv_opt & NES_DRV_OPT_DUAL_LOGICAL_PORT)
+ nes_write_indexed(nesdev, NES_IDX_TX_POOL_SIZE, 0x00000002);
+ else
+ nes_write_indexed(nesdev, NES_IDX_TX_POOL_SIZE, 0x00000003);
+ } else {
+ if (nesadapter->port_count == 2)
+ u32temp = 0x00000044;
+ else
+ u32temp = 0x000000e4;
+ nes_write_indexed(nesdev, NES_IDX_TX_POOL_SIZE, 0x00000003);
+ }
+
+ nes_write_indexed(nesdev, NES_IDX_NIC_LOGPORT_TO_PHYPORT, u32temp);
+ nes_debug(NES_DBG_INIT, "Probe time, LOG2PHY=%u\n",
+ nes_read_indexed(nesdev, NES_IDX_NIC_LOGPORT_TO_PHYPORT));
+
+ spin_lock_init(&nesadapter->resource_lock);
+ spin_lock_init(&nesadapter->phy_lock);
+ spin_lock_init(&nesadapter->pbl_lock);
+ spin_lock_init(&nesadapter->periodic_timer_lock);
+
+ INIT_LIST_HEAD(&nesadapter->nesvnic_list[0]);
+ INIT_LIST_HEAD(&nesadapter->nesvnic_list[1]);
+ INIT_LIST_HEAD(&nesadapter->nesvnic_list[2]);
+ INIT_LIST_HEAD(&nesadapter->nesvnic_list[3]);
+
+ if ((!nesadapter->OneG_Mode) && (nesadapter->port_count == 2)) {
+ u32 pcs_control_status0, pcs_control_status1;
+ u32 reset_value;
+ u32 i = 0;
+ u32 int_cnt = 0;
+ u32 ext_cnt = 0;
+ unsigned long flags;
+ u32 j = 0;
+
+ pcs_control_status0 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0);
+ pcs_control_status1 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 + 0x200);
+
+ for (i = 0; i < NES_MAX_LINK_CHECK; i++) {
+ pcs_control_status0 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0);
+ pcs_control_status1 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 + 0x200);
+ if ((0x0F000100 == (pcs_control_status0 & 0x0F000100))
+ || (0x0F000100 == (pcs_control_status1 & 0x0F000100)))
+ int_cnt++;
+ msleep(1);
+ }
+ if (int_cnt > 1) {
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x0000F088);
+ mh_detected++;
+ reset_value = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
+ reset_value |= 0x0000003d;
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, reset_value);
+
+ while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET)
+ & 0x00000040) != 0x00000040) && (j++ < 5000));
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+
+ pcs_control_status0 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0);
+ pcs_control_status1 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 + 0x200);
+
+ for (i = 0; i < NES_MAX_LINK_CHECK; i++) {
+ pcs_control_status0 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0);
+ pcs_control_status1 = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 + 0x200);
+ if ((0x0F000100 == (pcs_control_status0 & 0x0F000100))
+ || (0x0F000100 == (pcs_control_status1 & 0x0F000100))) {
+ if (++ext_cnt > int_cnt) {
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1,
+ 0x0000F0C8);
+ mh_detected++;
+ reset_value = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
+ reset_value |= 0x0000003d;
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, reset_value);
+
+ while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET)
+ & 0x00000040) != 0x00000040) && (j++ < 5000));
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+ break;
+ }
+ }
+ msleep(1);
+ }
+ }
+ }
+
+ if (nesadapter->hw_rev == NE020_REV) {
+ init_timer(&nesadapter->mh_timer);
+ nesadapter->mh_timer.function = nes_mh_fix;
+ nesadapter->mh_timer.expires = jiffies + (HZ/5); /* 1 second */
+ nesadapter->mh_timer.data = (unsigned long)nesdev;
+ add_timer(&nesadapter->mh_timer);
+ } else {
+ nes_write32(nesdev->regs+NES_INTF_INT_STAT, 0x0f000000);
+ }
+
+ init_timer(&nesadapter->lc_timer);
+ nesadapter->lc_timer.function = nes_clc;
+ nesadapter->lc_timer.expires = jiffies + 3600 * HZ; /* 1 hour */
+ nesadapter->lc_timer.data = (unsigned long)nesdev;
+ add_timer(&nesadapter->lc_timer);
+
+ list_add_tail(&nesadapter->list, &nes_adapter_list);
+
+ for (func_index = 0; func_index < 8; func_index++) {
+ pci_bus_read_config_word(nesdev->pcidev->bus,
+ PCI_DEVFN(PCI_SLOT(nesdev->pcidev->devfn),
+ func_index), 0, &vendor_id);
+ if (vendor_id == 0xffff)
+ break;
+ }
+ nes_debug(NES_DBG_INIT, "%s %d functions found for %s.\n", __FUNCTION__,
+ func_index, pci_name(nesdev->pcidev));
+ nesadapter->adapter_fcn_count = func_index;
+
+ return nesadapter;
+}
+
+
+/**
+ * nes_reset_adapter_ne020
+ */
+unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_Mode)
+{
+ u32 port_count;
+ u32 u32temp;
+ u32 i;
+
+ u32temp = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
+ port_count = ((u32temp & 0x00000300) >> 8) + 1;
+ /* TODO: assuming that both SERDES are set the same for now */
+ *OneG_Mode = (u32temp & 0x00003c00) ? 0 : 1;
+ nes_debug(NES_DBG_INIT, "Initial Software Reset = 0x%08X, port_count=%u\n",
+ u32temp, port_count);
+ if (*OneG_Mode)
+ nes_debug(NES_DBG_INIT, "Running in 1G mode.\n");
+ u32temp &= 0xff00ffc0;
+ switch (port_count) {
+ case 1:
+ u32temp |= 0x00ee0000;
+ break;
+ case 2:
+ u32temp |= 0x00cc0000;
+ break;
+ case 4:
+ u32temp |= 0x00000000;
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ /* check and do full reset if needed */
+ if (nes_read_indexed(nesdev, NES_IDX_QP_CONTROL+(PCI_FUNC(nesdev->pcidev->devfn)*8))) {
+ nes_debug(NES_DBG_INIT, "Issuing Full Soft reset = 0x%08X\n", u32temp | 0xd);
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, u32temp | 0xd);
+
+ i = 0;
+ while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET) & 0x00000040) == 0) && i++ < 10000)
+ mdelay(1);
+ if (i >= 10000) {
+ nes_debug(NES_DBG_INIT, "Did not see full soft reset done.\n");
+ return 0;
+ }
+ }
+
+ /* port reset */
+ switch (port_count) {
+ case 1:
+ u32temp |= 0x00ee0010;
+ break;
+ case 2:
+ u32temp |= 0x00cc0030;
+ break;
+ case 4:
+ u32temp |= 0x00000030;
+ break;
+ }
+
+ nes_debug(NES_DBG_INIT, "Issuing Port Soft reset = 0x%08X\n", u32temp | 0xd);
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, u32temp | 0xd);
+
+ i = 0;
+ while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET) & 0x00000040) == 0) && i++ < 10000)
+ mdelay(1);
+ if (i >= 10000) {
+ nes_debug(NES_DBG_INIT, "Did not see port soft reset done.\n");
+ return 0;
+ }
+
+ /* serdes 0 */
+ i = 0;
+ while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0)
+ & 0x0000000f)) != 0x0000000f) && i++ < 5000)
+ mdelay(1);
+ if (i >= 5000) {
+ nes_debug(NES_DBG_INIT, "Serdes 0 not ready, status=%x\n", u32temp);
+ return 0;
+ }
+
+ /* serdes 1 */
+ if (port_count > 1) {
+ i = 0;
+ while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS1)
+ & 0x0000000f)) != 0x0000000f) && i++ < 5000)
+ mdelay(1);
+ if (i >= 5000) {
+ nes_debug(NES_DBG_INIT, "Serdes 1 not ready, status=%x\n", u32temp);
+ return 0;
+ }
+ }
+
+
+
+ i = 0;
+ while ((nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS) != 0x80) && i++ < 10000)
+ mdelay(1);
+ if (i >= 10000) {
+ printk(KERN_ERR PFX "Internal CPU not ready, status = %02X\n",
+ nes_read_indexed(nesdev, NES_IDX_INT_CPU_STATUS));
+ return 0;
+ }
+
+ return port_count;
+}
+
+
+/**
+ * nes_init_serdes
+ */
+int nes_init_serdes(struct nes_device *nesdev, u8 hw_rev, u8 port_count, u8 OneG_Mode)
+{
+ int i;
+ u32 u32temp;
+
+ if (hw_rev != NE020_REV) {
+ /* init serdes 0 */
+
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL0, 0x000000FF);
+ if (!OneG_Mode)
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_HIGHZ_LANE_MODE0, 0x11110000);
+ if (port_count > 1) {
+ /* init serdes 1 */
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL1, 0x000000FF);
+ if (!OneG_Mode)
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_HIGHZ_LANE_MODE1, 0x11110000);
+ }
+ } else {
+ /* init serdes 0 */
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL0, 0x00000008);
+ i = 0;
+ while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0)
+ & 0x0000000f)) != 0x0000000f) && i++ < 5000)
+ mdelay(1);
+ if (i >= 5000) {
+ nes_debug(NES_DBG_PHY, "Init: serdes 0 not ready, status=%x\n", u32temp);
+ return 1;
+ }
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_EMP0, 0x000bdef7);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_DRIVE0, 0x9ce73000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_MODE0, 0x0ff00000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_SIGDET0, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_BYPASS0, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_LOOPBACK_CONTROL0, 0x00000000);
+ if (OneG_Mode)
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_EQ_CONTROL0, 0xf0182222);
+ else
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_EQ_CONTROL0, 0xf0042222);
+
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL0, 0x000000ff);
+ if (port_count > 1) {
+ /* init serdes 1 */
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x00000048);
+ i = 0;
+ while (((u32temp = (nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS1)
+ & 0x0000000f)) != 0x0000000f) && (i++ < 5000))
+ mdelay(1);
+ if (i >= 5000) {
+ printk("%s: Init: serdes 1 not ready, status=%x\n", __FUNCTION__, u32temp);
+ /* return 1; */
+ }
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_EMP1, 0x000bdef7);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_DRIVE1, 0x9ce73000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_MODE1, 0x0ff00000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_SIGDET1, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_BYPASS1, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_LOOPBACK_CONTROL1, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_EQ_CONTROL1, 0xf0002222);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL1, 0x000000ff);
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * nes_init_csr_ne020
+ * Initialize registers for ne020 hardware
+ */
+void nes_init_csr_ne020(struct nes_device *nesdev, u8 hw_rev, u8 port_count)
+{
+ u32 u32temp;
+
+ nes_debug(NES_DBG_INIT, "port_count=%d\n", port_count);
+
+ nes_write_indexed(nesdev, 0x000001E4, 0x00000007);
+ /* nes_write_indexed(nesdev, 0x000001E8, 0x000208C4); */
+ nes_write_indexed(nesdev, 0x000001E8, 0x00020874);
+ nes_write_indexed(nesdev, 0x000001D8, 0x00048002);
+ /* nes_write_indexed(nesdev, 0x000001D8, 0x0004B002); */
+ nes_write_indexed(nesdev, 0x000001FC, 0x00050005);
+ nes_write_indexed(nesdev, 0x00000600, 0x55555555);
+ nes_write_indexed(nesdev, 0x00000604, 0x55555555);
+
+ /* TODO: move these MAC register settings to NIC bringup */
+ nes_write_indexed(nesdev, 0x00002000, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002004, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002008, 0x0000FFFF);
+ nes_write_indexed(nesdev, 0x0000200C, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002010, 0x000003c1);
+ nes_write_indexed(nesdev, 0x0000201C, 0x75345678);
+ if (port_count > 1) {
+ nes_write_indexed(nesdev, 0x00002200, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002204, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002208, 0x0000FFFF);
+ nes_write_indexed(nesdev, 0x0000220C, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002210, 0x000003c1);
+ nes_write_indexed(nesdev, 0x0000221C, 0x75345678);
+ nes_write_indexed(nesdev, 0x00000908, 0x20000001);
+ }
+ if (port_count > 2) {
+ nes_write_indexed(nesdev, 0x00002400, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002404, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002408, 0x0000FFFF);
+ nes_write_indexed(nesdev, 0x0000240C, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002410, 0x000003c1);
+ nes_write_indexed(nesdev, 0x0000241C, 0x75345678);
+ nes_write_indexed(nesdev, 0x00000910, 0x20000001);
+
+ nes_write_indexed(nesdev, 0x00002600, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002604, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002608, 0x0000FFFF);
+ nes_write_indexed(nesdev, 0x0000260C, 0x00000001);
+ nes_write_indexed(nesdev, 0x00002610, 0x000003c1);
+ nes_write_indexed(nesdev, 0x0000261C, 0x75345678);
+ nes_write_indexed(nesdev, 0x00000918, 0x20000001);
+ }
+
+ nes_write_indexed(nesdev, 0x00005000, 0x00018000);
+ /* nes_write_indexed(nesdev, 0x00005000, 0x00010000); */
+ nes_write_indexed(nesdev, 0x00005004, 0x00020001);
+ nes_write_indexed(nesdev, 0x00005008, 0x1F1F1F1F);
+ nes_write_indexed(nesdev, 0x00005010, 0x1F1F1F1F);
+ nes_write_indexed(nesdev, 0x00005018, 0x1F1F1F1F);
+ nes_write_indexed(nesdev, 0x00005020, 0x1F1F1F1F);
+ nes_write_indexed(nesdev, 0x00006090, 0xFFFFFFFF);
+
+ /* TODO: move this to code, get from EEPROM */
+ nes_write_indexed(nesdev, 0x00000900, 0x20000001);
+ nes_write_indexed(nesdev, 0x000060C0, 0x0000028e);
+ nes_write_indexed(nesdev, 0x000060C8, 0x00000020);
+ //
+ nes_write_indexed(nesdev, 0x000001EC, 0x7b2625a0);
+ /* nes_write_indexed(nesdev, 0x000001EC, 0x5f2625a0); */
+
+ if (hw_rev != NE020_REV) {
+ u32temp = nes_read_indexed(nesdev, 0x000008e8);
+ u32temp |= 0x80000000;
+ nes_write_indexed(nesdev, 0x000008e8, u32temp);
+ u32temp = nes_read_indexed(nesdev, 0x000021f8);
+ u32temp &= 0x7fffffff;
+ u32temp |= 0x7fff0010;
+ nes_write_indexed(nesdev, 0x000021f8, u32temp);
+ }
+}
+
+
+/**
+ * nes_destroy_adapter - destroy the adapter structure
+ */
+void nes_destroy_adapter(struct nes_adapter *nesadapter)
+{
+ struct nes_adapter *tmp_adapter;
+
+ list_for_each_entry(tmp_adapter, &nes_adapter_list, list) {
+ nes_debug(NES_DBG_SHUTDOWN, "Nes Adapter list entry = 0x%p.\n",
+ tmp_adapter);
+ }
+
+ nesadapter->ref_count--;
+ if (!nesadapter->ref_count) {
+ if (nesadapter->hw_rev == NE020_REV) {
+ del_timer(&nesadapter->mh_timer);
+ }
+ del_timer(&nesadapter->lc_timer);
+
+ list_del(&nesadapter->list);
+ kfree(nesadapter);
+ }
+}
+
+
+/**
+ * nes_init_cqp
+ */
+int nes_init_cqp(struct nes_device *nesdev)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_cqp_qp_context *cqp_qp_context;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_hw_ceq *ceq;
+ struct nes_hw_ceq *nic_ceq;
+ struct nes_hw_aeq *aeq;
+ void *vmem;
+ dma_addr_t pmem;
+ u32 count=0;
+ u32 cqp_head;
+ u64 u64temp;
+ u32 u32temp;
+
+ /* allocate CQP memory */
+ /* Need to add max_cq to the aeq size once cq overflow checking is added back */
+ /* SQ is 512 byte aligned, others are 256 byte aligned */
+ nesdev->cqp_mem_size = 512 +
+ (sizeof(struct nes_hw_cqp_wqe) * NES_CQP_SQ_SIZE) +
+ (sizeof(struct nes_hw_cqe) * NES_CCQ_SIZE) +
+ max(((u32)sizeof(struct nes_hw_ceqe) * NES_CCEQ_SIZE), (u32)256) +
+ max(((u32)sizeof(struct nes_hw_ceqe) * NES_NIC_CEQ_SIZE), (u32)256) +
+ (sizeof(struct nes_hw_aeqe) * nesadapter->max_qp) +
+ sizeof(struct nes_hw_cqp_qp_context);
+
+ nesdev->cqp_vbase = pci_alloc_consistent(nesdev->pcidev, nesdev->cqp_mem_size,
+ &nesdev->cqp_pbase);
+ if (!nesdev->cqp_vbase) {
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory for host descriptor rings\n");
+ return -ENOMEM;
+ }
+ memset(nesdev->cqp_vbase, 0, nesdev->cqp_mem_size);
+
+ /* Allocate a twice the number of CQP requests as the SQ size */
+ nesdev->nes_cqp_requests = kzalloc(sizeof(struct nes_cqp_request) *
+ 2 * NES_CQP_SQ_SIZE, GFP_KERNEL);
+ if (nesdev->nes_cqp_requests == NULL) {
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory CQP request entries.\n");
+ pci_free_consistent(nesdev->pcidev, nesdev->cqp_mem_size, nesdev->cqp.sq_vbase,
+ nesdev->cqp.sq_pbase);
+ return -ENOMEM;
+ }
+
+ nes_debug(NES_DBG_INIT, "Allocated CQP structures at %p (phys = %016lX), size = %u.\n",
+ nesdev->cqp_vbase, (unsigned long)nesdev->cqp_pbase, nesdev->cqp_mem_size);
+
+ spin_lock_init(&nesdev->cqp.lock);
+ init_waitqueue_head(&nesdev->cqp.waitq);
+
+ /* Setup Various Structures */
+ vmem = (void *)(((unsigned long)nesdev->cqp_vbase + (512 - 1)) &
+ ~(unsigned long)(512 - 1));
+ pmem = (dma_addr_t)(((unsigned long long)nesdev->cqp_pbase + (512 - 1)) &
+ ~(unsigned long long)(512 - 1));
+
+ nesdev->cqp.sq_vbase = vmem;
+ nesdev->cqp.sq_pbase = pmem;
+ nesdev->cqp.sq_size = NES_CQP_SQ_SIZE;
+ nesdev->cqp.sq_head = 0;
+ nesdev->cqp.sq_tail = 0;
+ nesdev->cqp.qp_id = PCI_FUNC(nesdev->pcidev->devfn);
+
+ vmem += (sizeof(struct nes_hw_cqp_wqe) * nesdev->cqp.sq_size);
+ pmem += (sizeof(struct nes_hw_cqp_wqe) * nesdev->cqp.sq_size);
+
+ nesdev->ccq.cq_vbase = vmem;
+ nesdev->ccq.cq_pbase = pmem;
+ nesdev->ccq.cq_size = NES_CCQ_SIZE;
+ nesdev->ccq.cq_head = 0;
+ nesdev->ccq.ce_handler = nes_cqp_ce_handler;
+ nesdev->ccq.cq_number = PCI_FUNC(nesdev->pcidev->devfn);
+
+ vmem += (sizeof(struct nes_hw_cqe) * nesdev->ccq.cq_size);
+ pmem += (sizeof(struct nes_hw_cqe) * nesdev->ccq.cq_size);
+
+ nesdev->ceq_index = PCI_FUNC(nesdev->pcidev->devfn);
+ ceq = &nesadapter->ceq[nesdev->ceq_index];
+ ceq->ceq_vbase = vmem;
+ ceq->ceq_pbase = pmem;
+ ceq->ceq_size = NES_CCEQ_SIZE;
+ ceq->ceq_head = 0;
+
+ vmem += max(((u32)sizeof(struct nes_hw_ceqe) * ceq->ceq_size), (u32)256);
+ pmem += max(((u32)sizeof(struct nes_hw_ceqe) * ceq->ceq_size), (u32)256);
+
+ nesdev->nic_ceq_index = PCI_FUNC(nesdev->pcidev->devfn) + 8;
+ nic_ceq = &nesadapter->ceq[nesdev->nic_ceq_index];
+ nic_ceq->ceq_vbase = vmem;
+ nic_ceq->ceq_pbase = pmem;
+ nic_ceq->ceq_size = NES_NIC_CEQ_SIZE;
+ nic_ceq->ceq_head = 0;
+
+ vmem += max(((u32)sizeof(struct nes_hw_ceqe) * nic_ceq->ceq_size), (u32)256);
+ pmem += max(((u32)sizeof(struct nes_hw_ceqe) * nic_ceq->ceq_size), (u32)256);
+
+ aeq = &nesadapter->aeq[PCI_FUNC(nesdev->pcidev->devfn)];
+ aeq->aeq_vbase = vmem;
+ aeq->aeq_pbase = pmem;
+ aeq->aeq_size = nesadapter->max_qp;
+ aeq->aeq_head = 0;
+
+ /* Setup QP Context */
+ vmem += (sizeof(struct nes_hw_aeqe) * aeq->aeq_size);
+ pmem += (sizeof(struct nes_hw_aeqe) * aeq->aeq_size);
+
+ cqp_qp_context = vmem;
+ cqp_qp_context->context_words[0] =
+ cpu_to_le32((PCI_FUNC(nesdev->pcidev->devfn) << 12) + (2 << 10));
+ cqp_qp_context->context_words[1] = 0;
+ cqp_qp_context->context_words[2] = cpu_to_le32((u32)nesdev->cqp.sq_pbase);
+ cqp_qp_context->context_words[3] = cpu_to_le32(((u64)nesdev->cqp.sq_pbase) >> 32);
+
+
+ /* Write the address to Create CQP */
+ if ((sizeof(dma_addr_t) > 4)) {
+ nes_write_indexed(nesdev,
+ NES_IDX_CREATE_CQP_HIGH + (PCI_FUNC(nesdev->pcidev->devfn) * 8),
+ ((u64)pmem) >> 32);
+ } else {
+ nes_write_indexed(nesdev,
+ NES_IDX_CREATE_CQP_HIGH + (PCI_FUNC(nesdev->pcidev->devfn) * 8), 0);
+ }
+ nes_write_indexed(nesdev,
+ NES_IDX_CREATE_CQP_LOW + (PCI_FUNC(nesdev->pcidev->devfn) * 8),
+ (u32)pmem);
+
+ INIT_LIST_HEAD(&nesdev->cqp_avail_reqs);
+ INIT_LIST_HEAD(&nesdev->cqp_pending_reqs);
+
+ for (count = 0; count < 2*NES_CQP_SQ_SIZE; count++) {
+ init_waitqueue_head(&nesdev->nes_cqp_requests[count].waitq);
+ list_add_tail(&nesdev->nes_cqp_requests[count].list, &nesdev->cqp_avail_reqs);
+ }
+
+ /* Write Create CCQ WQE */
+ cqp_head = nesdev->cqp.sq_head++;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_CREATE_CQ | NES_CQP_CQ_CEQ_VALID |
+ NES_CQP_CQ_CHK_OVERFLOW | ((u32)nesdev->ccq.cq_size << 16)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ (nesdev->ccq.cq_number |
+ ((u32)nesdev->ceq_index << 16)));
+ u64temp = (u64)nesdev->ccq.cq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] = 0;
+ u64temp = (unsigned long)&nesdev->ccq;
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_LOW_IDX] =
+ cpu_to_le32((u32)(u64temp >> 1));
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] =
+ cpu_to_le32(((u32)((u64temp) >> 33)) & 0x7FFFFFFF);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX] = 0;
+
+ /* Write Create CEQ WQE */
+ cqp_head = nesdev->cqp.sq_head++;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_CREATE_CEQ + ((u32)nesdev->ceq_index << 8)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_CEQ_WQE_ELEMENT_COUNT_IDX, ceq->ceq_size);
+ u64temp = (u64)ceq->ceq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+
+ /* Write Create AEQ WQE */
+ cqp_head = nesdev->cqp.sq_head++;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_CREATE_AEQ + ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 8)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_AEQ_WQE_ELEMENT_COUNT_IDX, aeq->aeq_size);
+ u64temp = (u64)aeq->aeq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+
+ /* Write Create NIC CEQ WQE */
+ cqp_head = nesdev->cqp.sq_head++;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_CREATE_CEQ + ((u32)nesdev->nic_ceq_index << 8)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_CEQ_WQE_ELEMENT_COUNT_IDX, nic_ceq->ceq_size);
+ u64temp = (u64)nic_ceq->ceq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+
+ /* Poll until CCQP done */
+ count = 0;
+ do {
+ if (count++ > 1000) {
+ printk(KERN_ERR PFX "Error creating CQP\n");
+ pci_free_consistent(nesdev->pcidev, nesdev->cqp_mem_size,
+ nesdev->cqp_vbase, nesdev->cqp_pbase);
+ return -1;
+ }
+ udelay(10);
+ } while (!(nes_read_indexed(nesdev,
+ NES_IDX_QP_CONTROL + (PCI_FUNC(nesdev->pcidev->devfn) * 8)) & (1 << 8)));
+
+ nes_debug(NES_DBG_INIT, "CQP Status = 0x%08X\n", nes_read_indexed(nesdev,
+ NES_IDX_QP_CONTROL+(PCI_FUNC(nesdev->pcidev->devfn)*8)));
+
+ u32temp = 0x04800000;
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, u32temp | nesdev->cqp.qp_id);
+
+ /* wait for the CCQ, CEQ, and AEQ to get created */
+ count = 0;
+ do {
+ if (count++ > 1000) {
+ printk(KERN_ERR PFX "Error creating CCQ, CEQ, and AEQ\n");
+ pci_free_consistent(nesdev->pcidev, nesdev->cqp_mem_size,
+ nesdev->cqp_vbase, nesdev->cqp_pbase);
+ return -1;
+ }
+ udelay(10);
+ } while (((nes_read_indexed(nesdev,
+ NES_IDX_QP_CONTROL+(PCI_FUNC(nesdev->pcidev->devfn)*8)) & (15<<8)) != (15<<8)));
+
+ /* dump the QP status value */
+ nes_debug(NES_DBG_INIT, "QP Status = 0x%08X\n", nes_read_indexed(nesdev,
+ NES_IDX_QP_CONTROL+(PCI_FUNC(nesdev->pcidev->devfn)*8)));
+
+ nesdev->cqp.sq_tail++;
+
+ return 0;
+}
+
+
+/**
+ * nes_destroy_cqp
+ */
+int nes_destroy_cqp(struct nes_device *nesdev)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 count = 0;
+ u32 cqp_head;
+ unsigned long flags;
+
+ do {
+ if (count++ > 1000)
+ break;
+ udelay(10);
+ } while (!(nesdev->cqp.sq_head == nesdev->cqp.sq_tail));
+
+ /* Reset CCQ */
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_RESET |
+ nesdev->ccq.cq_number);
+
+ /* Disable device interrupts */
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x7fffffff);
+
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+
+ /* Destroy the AEQ */
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_DESTROY_AEQ |
+ ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 8));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = 0;
+
+ /* Destroy the NIC CEQ */
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_DESTROY_CEQ |
+ ((u32)nesdev->nic_ceq_index << 8));
+
+ /* Destroy the CEQ */
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_DESTROY_CEQ |
+ (nesdev->ceq_index << 8));
+
+ /* Destroy the CCQ */
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_DESTROY_CQ);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(nesdev->ccq.cq_number |
+ ((u32)nesdev->ceq_index << 16));
+
+ /* Destroy CQP */
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_DESTROY_QP |
+ NES_CQP_QP_TYPE_CQP);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(nesdev->cqp.qp_id);
+
+ barrier();
+ /* Ring doorbell (5 WQEs) */
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x05800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+
+ /* wait for the CCQ, CEQ, and AEQ to get destroyed */
+ count = 0;
+ do {
+ if (count++ > 1000) {
+ printk(KERN_ERR PFX "Function%d: Error destroying CCQ, CEQ, and AEQ\n",
+ PCI_FUNC(nesdev->pcidev->devfn));
+ break;
+ }
+ udelay(10);
+ } while (((nes_read_indexed(nesdev,
+ NES_IDX_QP_CONTROL + (PCI_FUNC(nesdev->pcidev->devfn)*8)) & (15 << 8)) != 0));
+
+ /* dump the QP status value */
+ nes_debug(NES_DBG_SHUTDOWN, "Function%d: QP Status = 0x%08X\n",
+ PCI_FUNC(nesdev->pcidev->devfn),
+ nes_read_indexed(nesdev,
+ NES_IDX_QP_CONTROL+(PCI_FUNC(nesdev->pcidev->devfn)*8)));
+
+ kfree(nesdev->nes_cqp_requests);
+
+ /* Free the control structures */
+ pci_free_consistent(nesdev->pcidev, nesdev->cqp_mem_size, nesdev->cqp.sq_vbase,
+ nesdev->cqp.sq_pbase);
+
+ return 0;
+}
+
+
+/**
+ * nes_init_phy
+ */
+int nes_init_phy(struct nes_device *nesdev)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 counter = 0;
+ u32 mac_index = nesdev->mac_index;
+ u32 tx_config;
+ u16 phy_data;
+
+ if (nesadapter->OneG_Mode) {
+ nes_debug(NES_DBG_PHY, "1G PHY, mac_index = %d.\n", mac_index);
+ if (nesadapter->phy_type[mac_index] == NES_PHY_TYPE_1G) {
+ printk(PFX "%s: Programming mdc config for 1G\n", __FUNCTION__);
+ tx_config = nes_read_indexed(nesdev, NES_IDX_MAC_TX_CONFIG);
+ tx_config |= 0x04;
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONFIG, tx_config);
+ }
+
+ nes_read_1G_phy_reg(nesdev, 1, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 1 phy address %u = 0x%X.\n",
+ nesadapter->phy_index[mac_index], phy_data);
+ nes_write_1G_phy_reg(nesdev, 23, nesadapter->phy_index[mac_index], 0xb000);
+
+ /* Reset the PHY */
+ nes_write_1G_phy_reg(nesdev, 0, nesadapter->phy_index[mac_index], 0x8000);
+ udelay(100);
+ counter = 0;
+ do {
+ nes_read_1G_phy_reg(nesdev, 0, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0 = 0x%X.\n", phy_data);
+ if (counter++ > 100) break;
+ } while (phy_data & 0x8000);
+
+ /* Setting no phy loopback */
+ phy_data &= 0xbfff;
+ phy_data |= 0x1140;
+ nes_write_1G_phy_reg(nesdev, 0, nesadapter->phy_index[mac_index], phy_data);
+ nes_read_1G_phy_reg(nesdev, 0, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0 = 0x%X.\n", phy_data);
+
+ nes_read_1G_phy_reg(nesdev, 0x17, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x17 = 0x%X.\n", phy_data);
+
+ nes_read_1G_phy_reg(nesdev, 0x1e, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x1e = 0x%X.\n", phy_data);
+
+ /* Setting the interrupt mask */
+ nes_read_1G_phy_reg(nesdev, 0x19, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x19 = 0x%X.\n", phy_data);
+ nes_write_1G_phy_reg(nesdev, 0x19, nesadapter->phy_index[mac_index], 0xffee);
+
+ nes_read_1G_phy_reg(nesdev, 0x19, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x19 = 0x%X.\n", phy_data);
+
+ /* turning on flow control */
+ nes_read_1G_phy_reg(nesdev, 4, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x4 = 0x%X.\n", phy_data);
+ nes_write_1G_phy_reg(nesdev, 4, nesadapter->phy_index[mac_index],
+ (phy_data & ~(0x03E0)) | 0xc00);
+ /* nes_write_1G_phy_reg(nesdev, 4, nesadapter->phy_index[mac_index],
+ phy_data | 0xc00); */
+ nes_read_1G_phy_reg(nesdev, 4, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x4 = 0x%X.\n", phy_data);
+
+ nes_read_1G_phy_reg(nesdev, 9, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x9 = 0x%X.\n", phy_data);
+ /* Clear Half duplex */
+ nes_write_1G_phy_reg(nesdev, 9, nesadapter->phy_index[mac_index],
+ phy_data & ~(0x0100));
+ nes_read_1G_phy_reg(nesdev, 9, nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy data from register 0x9 = 0x%X.\n", phy_data);
+
+ nes_read_1G_phy_reg(nesdev, 0, nesadapter->phy_index[mac_index], &phy_data);
+ nes_write_1G_phy_reg(nesdev, 0, nesadapter->phy_index[mac_index], phy_data | 0x0300);
+ } else {
+ if (nesadapter->phy_type[mac_index] == NES_PHY_TYPE_IRIS) {
+ /* setup 10G MDIO operation */
+ tx_config = nes_read_indexed(nesdev, NES_IDX_MAC_TX_CONFIG);
+ tx_config |= 0x14;
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONFIG, tx_config);
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * nes_replenish_nic_rq
+ */
+static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)
+{
+ unsigned long flags;
+ dma_addr_t bus_address;
+ struct sk_buff *skb;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ struct nes_hw_nic *nesnic;
+ struct nes_device *nesdev;
+ u32 rx_wqes_posted = 0;
+
+ nesnic = &nesvnic->nic;
+ nesdev = nesvnic->nesdev;
+ spin_lock_irqsave(&nesnic->rq_lock, flags);
+ if (nesnic->replenishing_rq !=0) {
+ if (((nesnic->rq_size-1) == atomic_read(&nesvnic->rx_skbs_needed)) &&
+ (atomic_read(&nesvnic->rx_skb_timer_running) == 0)) {
+ atomic_set(&nesvnic->rx_skb_timer_running, 1);
+ spin_unlock_irqrestore(&nesnic->rq_lock, flags);
+ nesvnic->rq_wqes_timer.expires = jiffies + (HZ/2); /* 1/2 second */
+ add_timer(&nesvnic->rq_wqes_timer);
+ } else
+ spin_unlock_irqrestore(&nesnic->rq_lock, flags);
+ return;
+ }
+ nesnic->replenishing_rq = 1;
+ spin_unlock_irqrestore(&nesnic->rq_lock, flags);
+ do {
+ skb = dev_alloc_skb(nesvnic->max_frame_size);
+ if (skb) {
+ skb->dev = nesvnic->netdev;
+
+ bus_address = pci_map_single(nesdev->pcidev,
+ skb->data, nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+
+ nic_rqe = &nesnic->rq_vbase[nesvnic->nic.rq_head];
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] =
+ cpu_to_le32(nesvnic->max_frame_size);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_3_2_IDX] = 0;
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX] =
+ cpu_to_le32((u32)bus_address);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX] =
+ cpu_to_le32((u32)((u64)bus_address >> 32));
+ nesnic->rx_skb[nesnic->rq_head] = skb;
+ nesnic->rq_head++;
+ nesnic->rq_head &= nesnic->rq_size - 1;
+ atomic_dec(&nesvnic->rx_skbs_needed);
+ barrier();
+ if (++rx_wqes_posted == 255) {
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, (rx_wqes_posted << 24) | nesnic->qp_id);
+ rx_wqes_posted = 0;
+ }
+ } else {
+ spin_lock_irqsave(&nesnic->rq_lock, flags);
+ if (((nesnic->rq_size-1) == atomic_read(&nesvnic->rx_skbs_needed)) &&
+ (atomic_read(&nesvnic->rx_skb_timer_running) == 0)) {
+ atomic_set(&nesvnic->rx_skb_timer_running, 1);
+ spin_unlock_irqrestore(&nesnic->rq_lock, flags);
+ nesvnic->rq_wqes_timer.expires = jiffies + (HZ/2); /* 1/2 second */
+ add_timer(&nesvnic->rq_wqes_timer);
+ } else
+ spin_unlock_irqrestore(&nesnic->rq_lock, flags);
+ break;
+ }
+ } while (atomic_read(&nesvnic->rx_skbs_needed));
+ barrier();
+ if (rx_wqes_posted)
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, (rx_wqes_posted << 24) | nesnic->qp_id);
+ nesnic->replenishing_rq = 0;
+}
+
+
+/**
+ * nes_rq_wqes_timeout
+ */
+static void nes_rq_wqes_timeout(unsigned long parm)
+{
+ struct nes_vnic *nesvnic = (struct nes_vnic *)parm;
+ printk("%s: Timer fired.\n", __FUNCTION__);
+ atomic_set(&nesvnic->rx_skb_timer_running, 0);
+ if (atomic_read(&nesvnic->rx_skbs_needed))
+ nes_replenish_nic_rq(nesvnic);
+}
+
+
+/**
+ * nes_init_nic_qp
+ */
+int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_hw_nic_sq_wqe *nic_sqe;
+ struct nes_hw_nic_qp_context *nic_context;
+ struct sk_buff *skb;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ unsigned long flags;
+ void *vmem;
+ dma_addr_t pmem;
+ u64 u64temp;
+ int ret;
+ u32 cqp_head;
+ u32 counter;
+ u32 wqe_count;
+ u8 jumbomode=0;
+
+ /* Allocate fragment, SQ, RQ, and CQ; Reuse CEQ based on the PCI function */
+ nesvnic->nic_mem_size = 256 +
+ (NES_NIC_WQ_SIZE * sizeof(struct nes_first_frag)) +
+ (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe)) +
+ (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_rq_wqe)) +
+ (NES_NIC_WQ_SIZE * 2 * sizeof(struct nes_hw_nic_cqe)) +
+ sizeof(struct nes_hw_nic_qp_context);
+
+ nesvnic->nic_vbase = pci_alloc_consistent(nesdev->pcidev, nesvnic->nic_mem_size,
+ &nesvnic->nic_pbase);
+ if (!nesvnic->nic_vbase) {
+ nes_debug(NES_DBG_INIT, "Unable to allocate memory for NIC host descriptor rings\n");
+ return -ENOMEM;
+ }
+ memset(nesvnic->nic_vbase, 0, nesvnic->nic_mem_size);
+ nes_debug(NES_DBG_INIT, "Allocated NIC QP structures at %p (phys = %016lX), size = %u.\n",
+ nesvnic->nic_vbase, (unsigned long)nesvnic->nic_pbase, nesvnic->nic_mem_size);
+
+ vmem = (void *)(((unsigned long)nesvnic->nic_vbase + (256 - 1)) &
+ ~(unsigned long)(256 - 1));
+ pmem = (dma_addr_t)(((unsigned long long)nesvnic->nic_pbase + (256 - 1)) &
+ ~(unsigned long long)(256 - 1));
+
+ /* Setup the first Fragment buffers */
+ nesvnic->nic.first_frag_vbase = vmem;
+
+ for (counter = 0; counter < NES_NIC_WQ_SIZE; counter++) {
+ nesvnic->nic.frag_paddr[counter] = pmem;
+ pmem += sizeof(struct nes_first_frag);
+ }
+
+ /* setup the SQ */
+ vmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_first_frag));
+
+ nesvnic->nic.sq_vbase = (void *)vmem;
+ nesvnic->nic.sq_pbase = pmem;
+ nesvnic->nic.sq_head = 0;
+ nesvnic->nic.sq_tail = 0;
+ nesvnic->nic.sq_size = NES_NIC_WQ_SIZE;
+ for (counter = 0; counter < NES_NIC_WQ_SIZE; counter++) {
+ nic_sqe = &nesvnic->nic.sq_vbase[counter];
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_MISC_IDX] =
+ cpu_to_le32(NES_NIC_SQ_WQE_DISABLE_CHKSUM |
+ NES_NIC_SQ_WQE_COMPLETION);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX] =
+ cpu_to_le32((u32)NES_FIRST_FRAG_SIZE << 16);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_LOW_IDX] =
+ cpu_to_le32((u32)nesvnic->nic.frag_paddr[counter]);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_HIGH_IDX] =
+ cpu_to_le32((u32)((u64)nesvnic->nic.frag_paddr[counter] >> 32));
+ }
+
+ nesvnic->get_cqp_request = nes_get_cqp_request;
+ nesvnic->post_cqp_request = nes_post_cqp_request;
+ nesvnic->mcrq_mcast_filter = NULL;
+
+ spin_lock_init(&nesvnic->nic.sq_lock);
+ spin_lock_init(&nesvnic->nic.rq_lock);
+
+ /* setup the RQ */
+ vmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe));
+ pmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_sq_wqe));
+
+
+ nesvnic->nic.rq_vbase = vmem;
+ nesvnic->nic.rq_pbase = pmem;
+ nesvnic->nic.rq_head = 0;
+ nesvnic->nic.rq_tail = 0;
+ nesvnic->nic.rq_size = NES_NIC_WQ_SIZE;
+
+ /* setup the CQ */
+ vmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_rq_wqe));
+ pmem += (NES_NIC_WQ_SIZE * sizeof(struct nes_hw_nic_rq_wqe));
+
+ if (nesdev->nesadapter->netdev_count > 2)
+ nesvnic->mcrq_qp_id = nesvnic->nic_index + 32;
+ else
+ nesvnic->mcrq_qp_id = nesvnic->nic.qp_id + 4;
+
+ nesvnic->nic_cq.cq_vbase = vmem;
+ nesvnic->nic_cq.cq_pbase = pmem;
+ nesvnic->nic_cq.cq_head = 0;
+ nesvnic->nic_cq.cq_size = NES_NIC_WQ_SIZE * 2;
+
+ nesvnic->nic_cq.ce_handler = nes_nic_napi_ce_handler;
+
+ /* Send CreateCQ request to CQP */
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ cqp_head = nesdev->cqp.sq_head;
+
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(
+ NES_CQP_CREATE_CQ | NES_CQP_CQ_CEQ_VALID |
+ ((u32)nesvnic->nic_cq.cq_size << 16));
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(
+ nesvnic->nic_cq.cq_number | ((u32)nesdev->nic_ceq_index << 16));
+ u64temp = (u64)nesvnic->nic_cq.cq_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] = 0;
+ u64temp = (unsigned long)&nesvnic->nic_cq;
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_LOW_IDX] = cpu_to_le32((u32)(u64temp >> 1));
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] =
+ cpu_to_le32(((u32)((u64temp) >> 33)) & 0x7FFFFFFF);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX] = 0;
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ /* Send CreateQP request to CQP */
+ nic_context = (void *)(&nesvnic->nic_cq.cq_vbase[nesvnic->nic_cq.cq_size]);
+ nic_context->context_words[NES_NIC_CTX_MISC_IDX] =
+ cpu_to_le32((u32)NES_NIC_CTX_SIZE |
+ ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 12));
+ nes_debug(NES_DBG_INIT, "RX_WINDOW_BUFFER_PAGE_TABLE_SIZE = 0x%08X, RX_WINDOW_BUFFER_SIZE = 0x%08X\n",
+ nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_PAGE_TABLE_SIZE),
+ nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_SIZE));
+ if (nes_read_indexed(nesdev, NES_IDX_RX_WINDOW_BUFFER_SIZE) != 0) {
+ nic_context->context_words[NES_NIC_CTX_MISC_IDX] |= cpu_to_le32(NES_NIC_BACK_STORE);
+ }
+
+ u64temp = (u64)nesvnic->nic.sq_pbase;
+ nic_context->context_words[NES_NIC_CTX_SQ_LOW_IDX] = cpu_to_le32((u32)u64temp);
+ nic_context->context_words[NES_NIC_CTX_SQ_HIGH_IDX] = cpu_to_le32((u32)(u64temp >> 32));
+ u64temp = (u64)nesvnic->nic.rq_pbase;
+ nic_context->context_words[NES_NIC_CTX_RQ_LOW_IDX] = cpu_to_le32((u32)u64temp);
+ nic_context->context_words[NES_NIC_CTX_RQ_HIGH_IDX] = cpu_to_le32((u32)(u64temp >> 32));
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(NES_CQP_CREATE_QP |
+ NES_CQP_QP_TYPE_NIC);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(nesvnic->nic.qp_id);
+ u64temp = (u64)nesvnic->nic_cq.cq_pbase +
+ (nesvnic->nic_cq.cq_size * sizeof(struct nes_hw_nic_cqe));
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+ nesdev->cqp.sq_head = cqp_head;
+
+ barrier();
+
+ /* Ring doorbell (2 WQEs) */
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x02800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ nes_debug(NES_DBG_INIT, "Waiting for create NIC QP%u to complete.\n",
+ nesvnic->nic.qp_id);
+
+ ret = wait_event_timeout(nesdev->cqp.waitq, (nesdev->cqp.sq_tail == cqp_head),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_INIT, "Create NIC QP%u completed, wait_event_timeout ret = %u.\n",
+ nesvnic->nic.qp_id, ret);
+ if (!ret) {
+ nes_debug(NES_DBG_INIT, "NIC QP%u create timeout expired\n", nesvnic->nic.qp_id);
+ pci_free_consistent(nesdev->pcidev, nesvnic->nic_mem_size, nesvnic->nic_vbase,
+ nesvnic->nic_pbase);
+ return -EIO;
+ }
+
+ /* Populate the RQ */
+ for (counter = 0; counter < (NES_NIC_WQ_SIZE - 1); counter++) {
+ skb = dev_alloc_skb(nesvnic->max_frame_size);
+ if (!skb) {
+ nes_debug(NES_DBG_INIT, "%s: out of memory for receive skb\n", netdev->name);
+
+ nes_destroy_nic_qp(nesvnic);
+ return -ENOMEM;
+ }
+
+ skb->dev = netdev;
+
+ pmem = pci_map_single(nesdev->pcidev, skb->data,
+ nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+
+ nic_rqe = &nesvnic->nic.rq_vbase[counter];
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = cpu_to_le32(nesvnic->max_frame_size);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_3_2_IDX] = 0;
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX] = cpu_to_le32((u32)pmem);
+ nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX] = cpu_to_le32((u32)((u64)pmem >> 32));
+ nesvnic->nic.rx_skb[counter] = skb;
+ }
+
+ wqe_count = NES_NIC_WQ_SIZE - 1;
+ nesvnic->nic.rq_head = wqe_count;
+ barrier();
+ do {
+ counter = min(wqe_count, ((u32)255));
+ wqe_count -= counter;
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, (counter << 24) | nesvnic->nic.qp_id);
+ } while (wqe_count);
+ init_timer(&nesvnic->rq_wqes_timer);
+ nesvnic->rq_wqes_timer.function = nes_rq_wqes_timeout;
+ nesvnic->rq_wqes_timer.data = (unsigned long)nesvnic;
+ nes_debug(NES_DBG_INIT, "NAPI support Enabled\n");
+
+ if (nesdev->nesadapter->et_use_adaptive_rx_coalesce)
+ {
+ nes_nic_init_timer(nesdev);
+ if (netdev->mtu > 1500)
+ jumbomode = 1;
+ nes_nic_init_timer_defaults(nesdev, jumbomode);
+ }
+
+ return 0;
+}
+
+
+/**
+ * nes_destroy_nic_qp
+ */
+void nes_destroy_nic_qp(struct nes_vnic *nesvnic)
+{
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ u64 wqe_frag;
+ u32 cqp_head;
+ unsigned long flags;
+ int ret;
+
+ /* Free remaining NIC receive buffers */
+ while (nesvnic->nic.rq_head != nesvnic->nic.rq_tail) {
+ nic_rqe = &nesvnic->nic.rq_vbase[nesvnic->nic.rq_tail];
+ wqe_frag = (u64)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX]);
+ wqe_frag |= ((u64)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX])) << 32;
+ pci_unmap_single(nesdev->pcidev, (dma_addr_t)wqe_frag,
+ nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(nesvnic->nic.rx_skb[nesvnic->nic.rq_tail++]);
+ nesvnic->nic.rq_tail &= (nesvnic->nic.rq_size - 1);
+ }
+
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+
+ /* Destroy NIC QP */
+ cqp_head = nesdev->cqp.sq_head;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_NIC));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ nesvnic->nic.qp_id);
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+
+ /* Destroy NIC CQ */
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ (NES_CQP_DESTROY_CQ | ((u32)nesvnic->nic_cq.cq_size << 16)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ (nesvnic->nic_cq.cq_number | ((u32)nesdev->nic_ceq_index << 16)));
+
+ if (++cqp_head >= nesdev->cqp.sq_size)
+ cqp_head = 0;
+
+ nesdev->cqp.sq_head = cqp_head;
+ barrier();
+
+ /* Ring doorbell (2 WQEs) */
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x02800000 | nesdev->cqp.qp_id);
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ nes_debug(NES_DBG_SHUTDOWN, "Waiting for CQP, cqp_head=%u, cqp.sq_head=%u,"
+ " cqp.sq_tail=%u, cqp.sq_size=%u\n",
+ cqp_head, nesdev->cqp.sq_head,
+ nesdev->cqp.sq_tail, nesdev->cqp.sq_size);
+
+ ret = wait_event_timeout(nesdev->cqp.waitq, (nesdev->cqp.sq_tail == cqp_head),
+ NES_EVENT_TIMEOUT);
+
+ nes_debug(NES_DBG_SHUTDOWN, "Destroy NIC QP returned, wait_event_timeout ret = %u, cqp_head=%u,"
+ " cqp.sq_head=%u, cqp.sq_tail=%u\n",
+ ret, cqp_head, nesdev->cqp.sq_head, nesdev->cqp.sq_tail);
+ if (!ret) {
+ nes_debug(NES_DBG_SHUTDOWN, "NIC QP%u destroy timeout expired\n",
+ nesvnic->nic.qp_id);
+ }
+
+ pci_free_consistent(nesdev->pcidev, nesvnic->nic_mem_size, nesvnic->nic_vbase,
+ nesvnic->nic_pbase);
+}
+
+/**
+ * nes_napi_isr
+ */
+int nes_napi_isr(struct nes_device *nesdev)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 int_stat;
+
+ if (nesdev->napi_isr_ran) {
+ /* interrupt status has already been read in ISR */
+ int_stat = nesdev->int_stat;
+ } else {
+ int_stat = nes_read32(nesdev->regs + NES_INT_STAT);
+ nesdev->int_stat = int_stat;
+ nesdev->napi_isr_ran = 1;
+ }
+
+ int_stat &= nesdev->int_req;
+ /* iff NIC, process here, else wait for DPC */
+ if ((int_stat) && ((int_stat & 0x0000ff00) == int_stat)) {
+ nesdev->napi_isr_ran = 0;
+ nes_write32(nesdev->regs+NES_INT_STAT,
+ (int_stat &
+ ~(NES_INT_INTF|NES_INT_TIMER|NES_INT_MAC0|NES_INT_MAC1|NES_INT_MAC2|NES_INT_MAC3)));
+
+ /* Process the CEQs */
+ nes_process_ceq(nesdev, &nesdev->nesadapter->ceq[nesdev->nic_ceq_index]);
+
+ if (unlikely((((nesadapter->et_rx_coalesce_usecs_irq) &&
+ (!nesadapter->et_use_adaptive_rx_coalesce)) ||
+ ((nesadapter->et_use_adaptive_rx_coalesce) &&
+ (nesdev->deepcq_count > nesadapter->et_pkt_rate_low)))) ) {
+ if ((nesdev->int_req & NES_INT_TIMER) == 0) {
+ /* Enable Periodic timer interrupts */
+ nesdev->int_req |= NES_INT_TIMER;
+ /* ack any pending periodic timer interrupts so we don't get an immediate interrupt */
+ /* TODO: need to also ack other unused periodic timer values, get from nesadapter */
+ nes_write32(nesdev->regs+NES_TIMER_STAT,
+ nesdev->timer_int_req | ~(nesdev->nesadapter->timer_int_req));
+ nes_write32(nesdev->regs+NES_INTF_INT_MASK,
+ ~(nesdev->intf_int_req | NES_INTF_PERIODIC_TIMER));
+ }
+
+ if (unlikely(nesadapter->et_use_adaptive_rx_coalesce))
+ {
+ nes_nic_init_timer(nesdev);
+ }
+ /* Enable interrupts, except CEQs */
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x0000ffff | (~nesdev->int_req));
+ } else {
+ /* Enable interrupts, make sure timer is off */
+ nesdev->int_req &= ~NES_INT_TIMER;
+ nes_write32(nesdev->regs+NES_INTF_INT_MASK, ~(nesdev->intf_int_req));
+ nes_write32(nesdev->regs+NES_INT_MASK, ~nesdev->int_req);
+ nesadapter->tune_timer.timer_in_use_old = 0;
+ }
+ nesdev->deepcq_count = 0;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/**
+ * nes_dpc
+ */
+void nes_dpc(unsigned long param)
+{
+ struct nes_device *nesdev = (struct nes_device *)param;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 counter;
+ u32 loop_counter = 0;
+ u32 int_status_bit;
+ u32 int_stat;
+ u32 timer_stat;
+ u32 temp_int_stat;
+ u32 intf_int_stat;
+ u32 debug_error;
+ u32 processed_intf_int = 0;
+ u16 processed_timer_int = 0;
+ u16 completion_ints = 0;
+ u16 timer_ints = 0;
+
+ /* nes_debug(NES_DBG_ISR, "\n"); */
+
+ do {
+ timer_stat = 0;
+ if (nesdev->napi_isr_ran) {
+ nesdev->napi_isr_ran = 0;
+ int_stat = nesdev->int_stat;
+ } else
+ int_stat = nes_read32(nesdev->regs+NES_INT_STAT);
+ if (processed_intf_int != 0)
+ int_stat &= nesdev->int_req & ~NES_INT_INTF;
+ else
+ int_stat &= nesdev->int_req;
+ if (processed_timer_int == 0) {
+ processed_timer_int = 1;
+ if (int_stat & NES_INT_TIMER) {
+ timer_stat = nes_read32(nesdev->regs + NES_TIMER_STAT);
+ if ((timer_stat & nesdev->timer_int_req) == 0) {
+ int_stat &= ~NES_INT_TIMER;
+ }
+ }
+ } else {
+ int_stat &= ~NES_INT_TIMER;
+ }
+
+ if (int_stat) {
+ if (int_stat & ~(NES_INT_INTF|NES_INT_TIMER|NES_INT_MAC0|
+ NES_INT_MAC1|NES_INT_MAC2|NES_INT_MAC3)) {
+ /* Ack the interrupts */
+ nes_write32(nesdev->regs+NES_INT_STAT,
+ (int_stat & ~(NES_INT_INTF|NES_INT_TIMER|NES_INT_MAC0|
+ NES_INT_MAC1|NES_INT_MAC2|NES_INT_MAC3)));
+ }
+
+ temp_int_stat = int_stat;
+ for (counter = 0, int_status_bit = 1; counter < 16; counter++) {
+ if (int_stat & int_status_bit) {
+ nes_process_ceq(nesdev, &nesadapter->ceq[counter]);
+ temp_int_stat &= ~int_status_bit;
+ completion_ints = 1;
+ }
+ if (!(temp_int_stat & 0x0000ffff))
+ break;
+ int_status_bit <<= 1;
+ }
+
+ /* Process the AEQ for this pci function */
+ int_status_bit = 1 << (16 + PCI_FUNC(nesdev->pcidev->devfn));
+ if (int_stat & int_status_bit) {
+ nes_process_aeq(nesdev, &nesadapter->aeq[PCI_FUNC(nesdev->pcidev->devfn)]);
+ }
+
+ /* Process the MAC interrupt for this pci function */
+ int_status_bit = 1 << (24 + nesdev->mac_index);
+ if (int_stat & int_status_bit) {
+ nes_process_mac_intr(nesdev, nesdev->mac_index);
+ }
+
+ if (int_stat & NES_INT_TIMER) {
+ if (timer_stat & nesdev->timer_int_req) {
+ nes_write32(nesdev->regs + NES_TIMER_STAT,
+ (timer_stat & nesdev->timer_int_req) |
+ ~(nesdev->nesadapter->timer_int_req));
+ timer_ints = 1;
+ }
+ }
+
+ if (int_stat & NES_INT_INTF) {
+ processed_intf_int = 1;
+ intf_int_stat = nes_read32(nesdev->regs+NES_INTF_INT_STAT);
+ intf_int_stat &= nesdev->intf_int_req;
+ if (NES_INTF_INT_CRITERR & intf_int_stat) {
+ debug_error = nes_read_indexed(nesdev, NES_IDX_DEBUG_ERROR_CONTROL_STATUS);
+ printk(KERN_ERR PFX "Critical Error reported by device!!! 0x%02X\n",
+ (u16)debug_error);
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_CONTROL_STATUS,
+ 0x01010000 | (debug_error & 0x0000ffff));
+ /* BUG(); */
+ if (crit_err_count++ > 10)
+ nes_write_indexed(nesdev, NES_IDX_DEBUG_ERROR_MASKS1, 1 << 0x17);
+ }
+ if (NES_INTF_INT_PCIERR & intf_int_stat) {
+ printk(KERN_ERR PFX "PCI Error reported by device!!!\n");
+ BUG();
+ }
+ if (NES_INTF_INT_AEQ_OFLOW & intf_int_stat) {
+ printk(KERN_ERR PFX "AEQ Overflow reported by device!!!\n");
+ BUG();
+ }
+ nes_write32(nesdev->regs+NES_INTF_INT_STAT, intf_int_stat);
+ }
+
+ if (int_stat & NES_INT_TSW) {
+ }
+ }
+ /* Don't use the interface interrupt bit stay in loop */
+ int_stat &= ~NES_INT_INTF|NES_INT_TIMER|NES_INT_MAC0|
+ NES_INT_MAC1|NES_INT_MAC2|NES_INT_MAC3;
+ } while ((int_stat != 0) && (loop_counter++ < MAX_DPC_ITERATIONS));
+
+ if (timer_ints == 1) {
+ if ((nesadapter->et_rx_coalesce_usecs_irq) || (nesadapter->et_use_adaptive_rx_coalesce)) {
+ if (completion_ints == 0) {
+ nesdev->timer_only_int_count++;
+ if (nesdev->timer_only_int_count>=nesadapter->timer_int_limit) {
+ nesdev->timer_only_int_count = 0;
+ nesdev->int_req &= ~NES_INT_TIMER;
+ nes_write32(nesdev->regs + NES_INTF_INT_MASK, ~(nesdev->intf_int_req));
+ nes_write32(nesdev->regs+NES_INT_MASK, ~nesdev->int_req);
+ nesdev->nesadapter->tune_timer.timer_in_use_old = 0;
+ } else {
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x0000ffff|(~nesdev->int_req));
+ }
+ } else {
+ if (unlikely(nesadapter->et_use_adaptive_rx_coalesce))
+ {
+ nes_nic_init_timer(nesdev);
+ }
+ nesdev->timer_only_int_count = 0;
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x0000ffff|(~nesdev->int_req));
+ }
+ } else {
+ nesdev->timer_only_int_count = 0;
+ nesdev->int_req &= ~NES_INT_TIMER;
+ nes_write32(nesdev->regs+NES_INTF_INT_MASK, ~(nesdev->intf_int_req));
+ nes_write32(nesdev->regs+NES_TIMER_STAT,
+ nesdev->timer_int_req | ~(nesdev->nesadapter->timer_int_req));
+ nes_write32(nesdev->regs+NES_INT_MASK, ~nesdev->int_req);
+ }
+ } else {
+ if ( (completion_ints == 1) &&
+ (((nesadapter->et_rx_coalesce_usecs_irq) &&
+ (!nesadapter->et_use_adaptive_rx_coalesce)) ||
+ ((nesdev->deepcq_count > nesadapter->et_pkt_rate_low) &&
+ (nesadapter->et_use_adaptive_rx_coalesce) )) ) {
+ /* nes_debug(NES_DBG_ISR, "Enabling periodic timer interrupt.\n" ); */
+ nesdev->timer_only_int_count = 0;
+ nesdev->int_req |= NES_INT_TIMER;
+ nes_write32(nesdev->regs+NES_TIMER_STAT,
+ nesdev->timer_int_req | ~(nesdev->nesadapter->timer_int_req));
+ nes_write32(nesdev->regs+NES_INTF_INT_MASK,
+ ~(nesdev->intf_int_req | NES_INTF_PERIODIC_TIMER));
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x0000ffff | (~nesdev->int_req));
+ } else {
+ nes_write32(nesdev->regs+NES_INT_MASK, ~nesdev->int_req);
+ }
+ }
+ nesdev->deepcq_count = 0;
+}
+
+
+/**
+ * nes_process_ceq
+ */
+void nes_process_ceq(struct nes_device *nesdev, struct nes_hw_ceq *ceq)
+{
+ u64 u64temp;
+ struct nes_hw_cq *cq;
+ u32 head;
+ u32 ceq_size;
+
+ /* nes_debug(NES_DBG_CQ, "\n"); */
+ head = ceq->ceq_head;
+ ceq_size = ceq->ceq_size;
+
+ do {
+ if (le32_to_cpu(ceq->ceq_vbase[head].ceqe_words[NES_CEQE_CQ_CTX_HIGH_IDX]) &
+ NES_CEQE_VALID) {
+ u64temp = (((u64)(le32_to_cpu(ceq->ceq_vbase[head].ceqe_words[NES_CEQE_CQ_CTX_HIGH_IDX])))<<32) |
+ ((u64)(le32_to_cpu(ceq->ceq_vbase[head].ceqe_words[NES_CEQE_CQ_CTX_LOW_IDX])));
+ u64temp <<= 1;
+ cq = *((struct nes_hw_cq **)&u64temp);
+ /* nes_debug(NES_DBG_CQ, "pCQ = %p\n", cq); */
+ barrier();
+ ceq->ceq_vbase[head].ceqe_words[NES_CEQE_CQ_CTX_HIGH_IDX] = 0;
+
+ /* call the event handler */
+ cq->ce_handler(nesdev, cq);
+
+ if (++head >= ceq_size)
+ head = 0;
+ } else {
+ break;
+ }
+
+ } while (1);
+
+ ceq->ceq_head = head;
+}
+
+
+/**
+ * nes_process_aeq
+ */
+void nes_process_aeq(struct nes_device *nesdev, struct nes_hw_aeq *aeq)
+{
+// u64 u64temp;
+ u32 head;
+ u32 aeq_size;
+ u32 aeqe_misc;
+ u32 aeqe_cq_id;
+ struct nes_hw_aeqe volatile *aeqe;
+
+ head = aeq->aeq_head;
+ aeq_size = aeq->aeq_size;
+
+ do {
+ aeqe = &aeq->aeq_vbase[head];
+ if ((le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]) & NES_AEQE_VALID) == 0)
+ break;
+ aeqe_misc = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]);
+ aeqe_cq_id = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]);
+ if (aeqe_misc & (NES_AEQE_QP|NES_AEQE_CQ)) {
+ if (aeqe_cq_id >= NES_FIRST_QPN) {
+ /* dealing with an accelerated QP related AE */
+// u64temp = (((u64)(le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])))<<32) |
+// ((u64)(le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX])));
+ nes_process_iwarp_aeqe(nesdev, (struct nes_hw_aeqe *)aeqe);
+ } else {
+ /* TODO: dealing with a CQP related AE */
+ nes_debug(NES_DBG_AEQ, "Processing CQP related AE, misc = 0x%04X\n",
+ (u16)(aeqe_misc >> 16));
+ }
+ }
+
+ aeqe->aeqe_words[NES_AEQE_MISC_IDX] = 0;
+
+ if (++head >= aeq_size)
+ head = 0;
+ }
+ while (1);
+ aeq->aeq_head = head;
+}
+
+static void nes_reset_link(struct nes_device *nesdev, u32 mac_index)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 reset_value;
+ u32 i=0;
+ u32 u32temp;
+
+ if (nesadapter->hw_rev == NE020_REV) {
+ return;
+ }
+ mh_detected++;
+
+ reset_value = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
+
+ if ((mac_index == 0) || ((mac_index == 1) && (nesadapter->OneG_Mode)))
+ reset_value |= 0x0000001d;
+ else
+ reset_value |= 0x0000002d;
+
+ if (4 <= (nesadapter->link_interrupt_count[mac_index] / ((u16)NES_MAX_LINK_INTERRUPTS))) {
+ if ((!nesadapter->OneG_Mode) && (nesadapter->port_count == 2)) {
+ nesadapter->link_interrupt_count[0] = 0;
+ nesadapter->link_interrupt_count[1] = 0;
+ u32temp = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1);
+ if (0x00000040 & u32temp)
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x0000F088);
+ else
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x0000F0C8);
+
+ reset_value |= 0x0000003d;
+ }
+ nesadapter->link_interrupt_count[mac_index] = 0;
+ }
+
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, reset_value);
+
+ while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET)
+ & 0x00000040) != 0x00000040) && (i++ < 5000));
+
+ if (0x0000003d == (reset_value & 0x0000003d)) {
+ u32 pcs_control_status0, pcs_control_status1;
+
+ for (i = 0; i < 10; i++) {
+ pcs_control_status0 = nes_read_indexed(nesdev, NES_IDX_PHY_PCS_CONTROL_STATUS0);
+ pcs_control_status1 = nes_read_indexed(nesdev, NES_IDX_PHY_PCS_CONTROL_STATUS0 + 0x200);
+ if (((0x0F000000 == (pcs_control_status0 & 0x0F000000))
+ && (pcs_control_status0 & 0x00100000))
+ || ((0x0F000000 == (pcs_control_status1 & 0x0F000000))
+ && (pcs_control_status1 & 0x00100000)))
+ continue;
+ else
+ break;
+ }
+ if (10 == i) {
+ u32temp = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1);
+ if (0x00000040 & u32temp)
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x0000F088);
+ else
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL1, 0x0000F0C8);
+
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, reset_value);
+
+ while (((nes_read32(nesdev->regs + NES_SOFTWARE_RESET)
+ & 0x00000040) != 0x00000040) && (i++ < 5000));
+ }
+ }
+}
+
+/**
+ * nes_process_mac_intr
+ */
+void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)
+{
+ unsigned long flags;
+ u32 pcs_control_status;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_vnic *nesvnic;
+ u32 mac_status;
+ u32 mac_index = nesdev->mac_index;
+ u32 u32temp;
+ u16 phy_data;
+ u16 temp_phy_data;
+
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+ if (nesadapter->mac_sw_state[mac_number] != NES_MAC_SW_IDLE) {
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+ return;
+ }
+ nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_INTERRUPT;
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+
+ /* ack the MAC interrupt */
+ mac_status = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS + (mac_index * 0x200));
+ /* Clear the interrupt */
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS + (mac_index * 0x200), mac_status);
+
+ nes_debug(NES_DBG_PHY, "MAC%u interrupt status = 0x%X.\n", mac_number, mac_status);
+
+ if (mac_status & (NES_MAC_INT_LINK_STAT_CHG | NES_MAC_INT_XGMII_EXT)) {
+ nesdev->link_status_interrupts++;
+ if (0 == (++nesadapter->link_interrupt_count[mac_index] % ((u16)NES_MAX_LINK_INTERRUPTS))) {
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+ nes_reset_link(nesdev, mac_index);
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+ }
+ /* read the PHY interrupt status register */
+ if (nesadapter->OneG_Mode) {
+ do {
+ nes_read_1G_phy_reg(nesdev, 0x1a,
+ nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy%d data from register 0x1a = 0x%X.\n",
+ nesadapter->phy_index[mac_index], phy_data);
+ } while (phy_data&0x8000);
+
+ temp_phy_data = 0;
+ do {
+ nes_read_1G_phy_reg(nesdev, 0x11,
+ nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy%d data from register 0x11 = 0x%X.\n",
+ nesadapter->phy_index[mac_index], phy_data);
+ if (temp_phy_data == phy_data)
+ break;
+ temp_phy_data = phy_data;
+ } while (1);
+
+ nes_read_1G_phy_reg(nesdev, 0x1e,
+ nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "Phy%d data from register 0x1e = 0x%X.\n",
+ nesadapter->phy_index[mac_index], phy_data);
+
+ nes_read_1G_phy_reg(nesdev, 1,
+ nesadapter->phy_index[mac_index], &phy_data);
+ nes_debug(NES_DBG_PHY, "1G phy%u data from register 1 = 0x%X\n",
+ nesadapter->phy_index[mac_index], phy_data);
+
+ if (temp_phy_data & 0x1000) {
+ nes_debug(NES_DBG_PHY, "The Link is up according to the PHY\n");
+ phy_data = 4;
+ } else {
+ nes_debug(NES_DBG_PHY, "The Link is down according to the PHY\n");
+ }
+ }
+ nes_debug(NES_DBG_PHY, "Eth SERDES Common Status: 0=0x%08X, 1=0x%08X\n",
+ nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0),
+ nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0+0x200));
+ pcs_control_status = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 + ((mac_index&1)*0x200));
+ pcs_control_status = nes_read_indexed(nesdev,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 + ((mac_index&1)*0x200));
+ nes_debug(NES_DBG_PHY, "PCS PHY Control/Status%u: 0x%08X\n",
+ mac_index, pcs_control_status);
+ if (nesadapter->OneG_Mode) {
+ u32temp = 0x01010000;
+ if (nesadapter->port_count > 2) {
+ u32temp |= 0x02020000;
+ }
+ if ((pcs_control_status & u32temp)!= u32temp) {
+ phy_data = 0;
+ nes_debug(NES_DBG_PHY, "PCS says the link is down\n");
+ }
+ } else if (nesadapter->phy_type[mac_index] == NES_PHY_TYPE_IRIS) {
+ nes_read_10G_phy_reg(nesdev, 1, nesadapter->phy_index[mac_index]);
+ temp_phy_data = (u16)nes_read_indexed(nesdev,
+ NES_IDX_MAC_MDIO_CONTROL);
+ u32temp = 20;
+ do {
+ nes_read_10G_phy_reg(nesdev, 1, nesadapter->phy_index[mac_index]);
+ phy_data = (u16)nes_read_indexed(nesdev,
+ NES_IDX_MAC_MDIO_CONTROL);
+ if ((phy_data == temp_phy_data) || (!(--u32temp)))
+ break;
+ temp_phy_data = phy_data;
+ } while (1);
+ nes_debug(NES_DBG_PHY, "%s: Phy data = 0x%04X, link was %s.\n",
+ __FUNCTION__, phy_data, nesadapter->mac_link_down ? "DOWN" : "UP");
+
+ } else {
+ phy_data = (0x0f0f0000 == (pcs_control_status & 0x0f1f0000)) ? 4 : 0;
+ }
+
+ if (phy_data & 0x0004) {
+ nesadapter->mac_link_down[mac_index] = 0;
+ list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) {
+ nes_debug(NES_DBG_PHY, "The Link is UP!!. linkup was %d\n",
+ nesvnic->linkup);
+ if (nesvnic->linkup == 0) {
+ printk(PFX "The Link is now up for port %u, netdev %p.\n",
+ mac_index, nesvnic->netdev);
+ if (netif_queue_stopped(nesvnic->netdev))
+ netif_start_queue(nesvnic->netdev);
+ nesvnic->linkup = 1;
+ netif_carrier_on(nesvnic->netdev);
+ }
+ }
+ } else {
+ nesadapter->mac_link_down[mac_index] = 1;
+ list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) {
+ nes_debug(NES_DBG_PHY, "The Link is Down!!. linkup was %d\n",
+ nesvnic->linkup);
+ if (nesvnic->linkup == 1) {
+ printk(PFX "The Link is now down for port %u, netdev %p.\n",
+ mac_index, nesvnic->netdev);
+ if (!(netif_queue_stopped(nesvnic->netdev)))
+ netif_stop_queue(nesvnic->netdev);
+ nesvnic->linkup = 0;
+ netif_carrier_off(nesvnic->netdev);
+ }
+ }
+ }
+ }
+
+ nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_IDLE;
+}
+
+
+
+void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
+{
+ struct nes_vnic *nesvnic = container_of(cq, struct nes_vnic, nic_cq);
+
+ netif_rx_schedule(nesdev->netdev[nesvnic->netdev_index], &nesvnic->napi);
+}
+
+
+/* The MAX_RQES_TO_PROCESS defines how many max read requests to complete before
+* getting out of nic_ce_handler
+*/
+#define MAX_RQES_TO_PROCESS 384
+
+/**
+ * nes_nic_ce_handler
+ */
+void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
+{
+ u64 u64temp;
+ dma_addr_t bus_address;
+ struct nes_hw_nic *nesnic;
+ struct nes_vnic *nesvnic = container_of(cq, struct nes_vnic, nic_cq);
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ struct nes_hw_nic_sq_wqe *nic_sqe;
+ struct sk_buff *skb;
+ struct sk_buff *rx_skb;
+ __le16 *wqe_fragment_length;
+ u32 head;
+ u32 cq_size;
+ u32 rx_pkt_size;
+ u32 cqe_count=0;
+ u32 cqe_errv;
+ u32 cqe_misc;
+ u16 wqe_fragment_index = 1; /* first fragment (0) is used by copy buffer */
+ u16 vlan_tag;
+ u16 pkt_type;
+ u16 rqes_processed = 0;
+ u8 sq_cqes = 0;
+
+ head = cq->cq_head;
+ cq_size = cq->cq_size;
+ cq->cqes_pending = 1;
+ do {
+ if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX]) &
+ NES_NIC_CQE_VALID) {
+ nesnic = &nesvnic->nic;
+ cqe_misc = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX]);
+ if (cqe_misc & NES_NIC_CQE_SQ) {
+ sq_cqes++;
+ wqe_fragment_index = 1;
+ nic_sqe = &nesnic->sq_vbase[nesnic->sq_tail];
+ skb = nesnic->tx_skb[nesnic->sq_tail];
+ wqe_fragment_length = (__le16 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX];
+ /* bump past the vlan tag */
+ wqe_fragment_length++;
+ if (le16_to_cpu(wqe_fragment_length[wqe_fragment_index]) != 0) {
+ u64temp = (u64) le32_to_cpu(nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_LOW_IDX+wqe_fragment_index*2]);
+ u64temp += ((u64)le32_to_cpu(nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_HIGH_IDX+wqe_fragment_index*2]))<<32;
+ bus_address = (dma_addr_t)u64temp;
+ if (test_and_clear_bit(nesnic->sq_tail, nesnic->first_frag_overflow)) {
+ pci_unmap_single(nesdev->pcidev,
+ bus_address,
+ le16_to_cpu(wqe_fragment_length[wqe_fragment_index++]),
+ PCI_DMA_TODEVICE);
+ }
+ for (; wqe_fragment_index < 5; wqe_fragment_index++) {
+ if (wqe_fragment_length[wqe_fragment_index]) {
+ u64temp = le32_to_cpu(nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_LOW_IDX+wqe_fragment_index*2]);
+ u64temp += ((u64)le32_to_cpu(nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_HIGH_IDX+wqe_fragment_index*2]))<<32;
+ bus_address = (dma_addr_t)u64temp;
+ pci_unmap_page(nesdev->pcidev,
+ bus_address,
+ le16_to_cpu(wqe_fragment_length[wqe_fragment_index]),
+ PCI_DMA_TODEVICE);
+ } else
+ break;
+ }
+ if (skb)
+ dev_kfree_skb_any(skb);
+ }
+ nesnic->sq_tail++;
+ nesnic->sq_tail &= nesnic->sq_size-1;
+ if (sq_cqes > 128) {
+ barrier();
+ /* restart the queue if it had been stopped */
+ if (netif_queue_stopped(nesvnic->netdev))
+ netif_wake_queue(nesvnic->netdev);
+ sq_cqes = 0;
+ }
+ } else {
+ rqes_processed ++;
+
+ cq->rx_cqes_completed++;
+ cq->rx_pkts_indicated++;
+ rx_pkt_size = cqe_misc & 0x0000ffff;
+ nic_rqe = &nesnic->rq_vbase[nesnic->rq_tail];
+ /* Get the skb */
+ rx_skb = nesnic->rx_skb[nesnic->rq_tail];
+ nic_rqe = &nesnic->rq_vbase[nesvnic->nic.rq_tail];
+ bus_address = (dma_addr_t)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX]);
+ bus_address += ((u64)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX])) << 32;
+ pci_unmap_single(nesdev->pcidev, bus_address,
+ nesvnic->max_frame_size, PCI_DMA_FROMDEVICE);
+ /* rx_skb->tail = rx_skb->data + rx_pkt_size; */
+ /* rx_skb->len = rx_pkt_size; */
+ rx_skb->len = 0; /* TODO: see if this is necessary */
+ skb_put(rx_skb, rx_pkt_size);
+ rx_skb->protocol = eth_type_trans(rx_skb, nesvnic->netdev);
+ nesnic->rq_tail++;
+ nesnic->rq_tail &= nesnic->rq_size - 1;
+
+ atomic_inc(&nesvnic->rx_skbs_needed);
+ if (atomic_read(&nesvnic->rx_skbs_needed) > (nesvnic->nic.rq_size>>1)) {
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
+ cq->cq_number | (cqe_count << 16));
+// nesadapter->tune_timer.cq_count += cqe_count;
+ nesdev->currcq_count += cqe_count;
+ cqe_count = 0;
+ nes_replenish_nic_rq(nesvnic);
+ }
+ pkt_type = (u16)(le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_NIC_CQE_TAG_PKT_TYPE_IDX]));
+ cqe_errv = (cqe_misc & NES_NIC_CQE_ERRV_MASK) >> NES_NIC_CQE_ERRV_SHIFT;
+ rx_skb->ip_summed = CHECKSUM_NONE;
+
+ if ((NES_PKT_TYPE_TCPV4_BITS == (pkt_type & NES_PKT_TYPE_TCPV4_MASK)) ||
+ (NES_PKT_TYPE_UDPV4_BITS == (pkt_type & NES_PKT_TYPE_UDPV4_MASK))) {
+ if ((cqe_errv &
+ (NES_NIC_ERRV_BITS_IPV4_CSUM_ERR | NES_NIC_ERRV_BITS_TCPUDP_CSUM_ERR |
+ NES_NIC_ERRV_BITS_IPH_ERR | NES_NIC_ERRV_BITS_WQE_OVERRUN)) == 0) {
+ if (nesvnic->rx_checksum_disabled == 0) {
+ rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+ } else
+ nes_debug(NES_DBG_CQ, "%s: unsuccessfully checksummed TCP or UDP packet."
+ " errv = 0x%X, pkt_type = 0x%X.\n",
+ nesvnic->netdev->name, cqe_errv, pkt_type);
+
+ } else if ((pkt_type & NES_PKT_TYPE_IPV4_MASK) == NES_PKT_TYPE_IPV4_BITS) {
+ if ((cqe_errv &
+ (NES_NIC_ERRV_BITS_IPV4_CSUM_ERR | NES_NIC_ERRV_BITS_IPH_ERR |
+ NES_NIC_ERRV_BITS_WQE_OVERRUN)) == 0) {
+ if (nesvnic->rx_checksum_disabled == 0) {
+ rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
+ /* nes_debug(NES_DBG_CQ, "%s: Reporting successfully checksummed IPv4 packet.\n",
+ nesvnic->netdev->name); */
+ }
+ } else
+ nes_debug(NES_DBG_CQ, "%s: unsuccessfully checksummed TCP or UDP packet."
+ " errv = 0x%X, pkt_type = 0x%X.\n",
+ nesvnic->netdev->name, cqe_errv, pkt_type);
+ }
+ /* nes_debug(NES_DBG_CQ, "pkt_type=%x, APBVT_MASK=%x\n",
+ pkt_type, (pkt_type & NES_PKT_TYPE_APBVT_MASK)); */
+
+ if ((pkt_type & NES_PKT_TYPE_APBVT_MASK) == NES_PKT_TYPE_APBVT_BITS) {
+ nes_cm_recv(rx_skb, nesvnic->netdev);
+ } else {
+ if ((cqe_misc & NES_NIC_CQE_TAG_VALID) && (nesvnic->vlan_grp != NULL)) {
+ vlan_tag = (u16)(le32_to_cpu(
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_TAG_PKT_TYPE_IDX])
+ >> 16);
+ nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",
+ nesvnic->netdev->name, vlan_tag);
+ nes_vlan_rx(rx_skb, nesvnic->vlan_grp, vlan_tag);
+ } else {
+ nes_netif_rx(rx_skb);
+ }
+ }
+
+ nesvnic->netdev->last_rx = jiffies;
+ /* nesvnic->netstats.rx_packets++; */
+ /* nesvnic->netstats.rx_bytes += rx_pkt_size; */
+ }
+
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_MISC_IDX] = 0;
+ /* Accounting... */
+ cqe_count++;
+ if (++head >= cq_size)
+ head = 0;
+ if (cqe_count == 255) {
+ /* Replenish Nic CQ */
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
+ cq->cq_number | (cqe_count << 16));
+// nesdev->nesadapter->tune_timer.cq_count += cqe_count;
+ nesdev->currcq_count += cqe_count;
+ cqe_count = 0;
+ }
+
+ if (cq->rx_cqes_completed >= nesvnic->budget)
+ break;
+ } else {
+ cq->cqes_pending = 0;
+ break;
+ }
+
+ } while (1);
+
+ if (sq_cqes) {
+ barrier();
+ /* restart the queue if it had been stopped */
+ if (netif_queue_stopped(nesvnic->netdev))
+ netif_wake_queue(nesvnic->netdev);
+ }
+
+ cq->cq_head = head;
+ /* nes_debug(NES_DBG_CQ, "CQ%u Processed = %u cqes, new head = %u.\n",
+ cq->cq_number, cqe_count, cq->cq_head); */
+ cq->cqe_allocs_pending = cqe_count;
+ if (unlikely(nesadapter->et_use_adaptive_rx_coalesce))
+ {
+// nesdev->nesadapter->tune_timer.cq_count += cqe_count;
+ nesdev->currcq_count += cqe_count;
+ nes_nic_tune_timer(nesdev);
+ }
+ if (atomic_read(&nesvnic->rx_skbs_needed))
+ nes_replenish_nic_rq(nesvnic);
+ }
+
+
+/**
+ * nes_cqp_ce_handler
+ */
+void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
+{
+ u64 u64temp;
+ unsigned long flags;
+ struct nes_hw_cqp *cqp = NULL;
+ struct nes_cqp_request *cqp_request;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 head;
+ u32 cq_size;
+ u32 cqe_count=0;
+ u32 error_code;
+ /* u32 counter; */
+
+ head = cq->cq_head;
+ cq_size = cq->cq_size;
+
+ do {
+ /* process the CQE */
+ /* nes_debug(NES_DBG_CQP, "head=%u cqe_words=%08X\n", head,
+ le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])); */
+
+ if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_VALID) {
+ u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head].
+ cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX])))<<32) |
+ ((u64)(le32_to_cpu(cq->cq_vbase[head].
+ cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX])));
+ cqp = *((struct nes_hw_cqp **)&u64temp);
+
+ error_code = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_ERROR_CODE_IDX]);
+ if (error_code) {
+ nes_debug(NES_DBG_CQP, "Bad Completion code for opcode 0x%02X from CQP,"
+ " Major/Minor codes = 0x%04X:%04X.\n",
+ le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])&0x3f,
+ (u16)(error_code >> 16),
+ (u16)error_code);
+ nes_debug(NES_DBG_CQP, "cqp: qp_id=%u, sq_head=%u, sq_tail=%u\n",
+ cqp->qp_id, cqp->sq_head, cqp->sq_tail);
+ }
+
+ u64temp = (((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail].
+ wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX])))<<32) |
+ ((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail].
+ wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX])));
+ cqp_request = *((struct nes_cqp_request **)&u64temp);
+ if (cqp_request) {
+ if (cqp_request->waiting) {
+ /* nes_debug(NES_DBG_CQP, "%s: Waking up requestor\n"); */
+ cqp_request->major_code = (u16)(error_code >> 16);
+ cqp_request->minor_code = (u16)error_code;
+ barrier();
+ cqp_request->request_done = 1;
+ wake_up(&cqp_request->waitq);
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X) freed.\n",
+ cqp_request,
+ le32_to_cpu(cqp_request->cqp_wqe.wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f);
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ } else if (cqp_request->callback) {
+ /* Envoke the callback routine */
+ cqp_request->cqp_callback(nesdev, cqp_request);
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ } else {
+ nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X) freed.\n",
+ cqp_request,
+ le32_to_cpu(cqp_request->cqp_wqe.wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f);
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ } else {
+ wake_up(&nesdev->cqp.waitq);
+ }
+
+ cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX] = 0;
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, cq->cq_number | (1 << 16));
+ if (++cqp->sq_tail >= cqp->sq_size)
+ cqp->sq_tail = 0;
+
+ /* Accounting... */
+ cqe_count++;
+ if (++head >= cq_size)
+ head = 0;
+ } else {
+ break;
+ }
+ } while (1);
+ cq->cq_head = head;
+
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ while ((!list_empty(&nesdev->cqp_pending_reqs)) &&
+ ((((nesdev->cqp.sq_tail+nesdev->cqp.sq_size)-nesdev->cqp.sq_head) &
+ (nesdev->cqp.sq_size - 1)) != 1)) {
+ cqp_request = list_entry(nesdev->cqp_pending_reqs.next,
+ struct nes_cqp_request, list);
+ list_del_init(&cqp_request->list);
+ head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[head];
+ memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));
+ barrier();
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] =
+ cpu_to_le32((u32)((unsigned long)cqp_request));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] =
+ cpu_to_le32((u32)(upper_32_bits((unsigned long)cqp_request)));
+ nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X) put on CQPs SQ wqe%u.\n",
+ cqp_request, le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f, head);
+ /* Ring doorbell (1 WQEs) */
+ barrier();
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x01800000 | nesdev->cqp.qp_id);
+ }
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+
+ /* Arm the CCQ */
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ cq->cq_number);
+ nes_read32(nesdev->regs+NES_CQE_ALLOC);
+}
+
+
+/**
+ * nes_process_iwarp_aeqe
+ */
+void nes_process_iwarp_aeqe(struct nes_device *nesdev, struct nes_hw_aeqe *aeqe)
+{
+ u64 context;
+ u64 aeqe_context = 0;
+ unsigned long flags;
+ struct nes_qp *nesqp;
+ int resource_allocated;
+ /* struct iw_cm_id *cm_id; */
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct ib_event ibevent;
+ /* struct iw_cm_event cm_event; */
+ u32 aeq_info;
+ u32 next_iwarp_state = 0;
+ u16 async_event_id;
+ u8 tcp_state;
+ u8 iwarp_state;
+
+ nes_debug(NES_DBG_AEQ, "\n");
+ aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]);
+ if ((NES_AEQE_INBOUND_RDMA&aeq_info) || (!(NES_AEQE_QP&aeq_info))) {
+ context = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX]);
+ context += ((u64)le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])) << 32;
+ } else {
+ aeqe_context = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX]);
+ aeqe_context += ((u64)le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])) << 32;
+ context = (unsigned long)nesadapter->qp_table[le32_to_cpu(
+ aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
+ BUG_ON(!context);
+ }
+
+ async_event_id = (u16)aeq_info;
+ tcp_state = (aeq_info & NES_AEQE_TCP_STATE_MASK) >> NES_AEQE_TCP_STATE_SHIFT;
+ iwarp_state = (aeq_info & NES_AEQE_IWARP_STATE_MASK) >> NES_AEQE_IWARP_STATE_SHIFT;
+ nes_debug(NES_DBG_AEQ, "aeid = 0x%04X, qp-cq id = %d, aeqe = %p,"
+ " Tcp state = %s, iWARP state = %s\n",
+ async_event_id,
+ le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]), aeqe,
+ nes_tcp_state_str[tcp_state], nes_iwarp_state_str[iwarp_state]);
+
+
+ switch (async_event_id) {
+ case NES_AEQE_AEID_LLP_FIN_RECEIVED:
+ nesqp = *((struct nes_qp **)&context);
+ if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
+ nesqp->cm_id->add_ref(nesqp->cm_id);
+ nes_add_ref(&nesqp->ibqp);
+ schedule_nes_timer(nesqp->cm_node, (struct sk_buff *)nesqp,
+ NES_TIMER_TYPE_CLOSE, 1, 0);
+ nes_debug(NES_DBG_AEQ, "QP%u Not decrementing QP refcount (%d),"
+ " need ae to finish up, original_last_aeq = 0x%04X."
+ " last_aeq = 0x%04X, scheduling timer. TCP state = %d\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ async_event_id, nesqp->last_aeq, tcp_state);
+ }
+ if ((tcp_state != NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
+ (nesqp->ibqp_state != IB_QPS_RTS)) {
+ /* FIN Received but tcp state or IB state moved on,
+ should expect a close complete */
+ return;
+ }
+ case NES_AEQE_AEID_LLP_CLOSE_COMPLETE:
+ case NES_AEQE_AEID_LLP_CONNECTION_RESET:
+ case NES_AEQE_AEID_TERMINATE_SENT:
+ case NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE:
+ case NES_AEQE_AEID_RESET_SENT:
+ nesqp = *((struct nes_qp **)&context);
+ if (async_event_id == NES_AEQE_AEID_RESET_SENT) {
+ tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ }
+ nes_add_ref(&nesqp->ibqp);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+
+ if ((tcp_state == NES_AEQE_TCP_STATE_CLOSED) ||
+ (tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT)) {
+ nesqp->hte_added = 0;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u to remove hte\n",
+ nesqp->hwqp.qp_id);
+ nes_hw_modify_qp(nesdev, nesqp,
+ NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_DEL_HTE, 0);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ }
+
+ if ((nesqp->ibqp_state == IB_QPS_RTS) &&
+ ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
+ (async_event_id == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+ switch (nesqp->hw_iwarp_state) {
+ case NES_AEQE_IWARP_STATE_RTS:
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_CLOSING;
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING;
+ break;
+ case NES_AEQE_IWARP_STATE_TERMINATE:
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE;
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_TERMINATE;
+ if (async_event_id == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) {
+ next_iwarp_state |= 0x02000000;
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ }
+ break;
+ default:
+ next_iwarp_state = 0;
+ }
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ if (next_iwarp_state) {
+ nes_add_ref(&nesqp->ibqp);
+ nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X,"
+ " also added another reference\n",
+ nesqp->hwqp.qp_id, next_iwarp_state);
+ nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0);
+ }
+ nes_cm_disconn(nesqp);
+ } else {
+ if (async_event_id == NES_AEQE_AEID_LLP_FIN_RECEIVED) {
+ /* FIN Received but ib state not RTS,
+ close complete will be on its way */
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_rem_ref(&nesqp->ibqp);
+ return;
+ }
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ if (async_event_id == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) {
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000;
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X,"
+ " also added another reference\n",
+ nesqp->hwqp.qp_id, next_iwarp_state);
+ nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0);
+ }
+ nes_cm_disconn(nesqp);
+ }
+ break;
+ case NES_AEQE_AEID_LLP_TERMINATE_RECEIVED:
+ nesqp = *((struct nes_qp **)&context);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TERMINATE_RECEIVED"
+ " event on QP%u \n Q2 Data:\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_FATAL;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
+ ((nesqp->ibqp_state == IB_QPS_RTS)&&
+ (async_event_id == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+ nes_add_ref(&nesqp->ibqp);
+ nes_cm_disconn(nesqp);
+ } else {
+ nesqp->in_disconnect = 0;
+ wake_up(&nesqp->kick_waitq);
+ }
+ break;
+ case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES:
+ nesqp = *((struct nes_qp **)&context);
+ nes_add_ref(&nesqp->ibqp);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_ERROR;
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ nesqp->last_aeq = async_event_id;
+ if (nesqp->cm_id) {
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TOO_MANY_RETRIES"
+ " event on QP%u, remote IP = 0x%08X \n",
+ nesqp->hwqp.qp_id,
+ ntohl(nesqp->cm_id->remote_addr.sin_addr.s_addr));
+ } else {
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TOO_MANY_RETRIES"
+ " event on QP%u \n",
+ nesqp->hwqp.qp_id);
+ }
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_RESET;
+ nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_FATAL;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ break;
+ case NES_AEQE_AEID_AMP_BAD_STAG_INDEX:
+ if (NES_AEQE_INBOUND_RDMA&aeq_info) {
+ nesqp = nesadapter->qp_table[le32_to_cpu(
+ aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
+ } else {
+ /* TODO: get the actual WQE and mask off wqe index */
+ context &= ~((u64)511);
+ nesqp = *((struct nes_qp **)&context);
+ }
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_AMP_BAD_STAG_INDEX event on QP%u\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_ACCESS_ERR;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ break;
+ case NES_AEQE_AEID_AMP_UNALLOCATED_STAG:
+ nesqp = *((struct nes_qp **)&context);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_AMP_UNALLOCATED_STAG event on QP%u\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_ACCESS_ERR;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ break;
+ case NES_AEQE_AEID_PRIV_OPERATION_DENIED:
+ nesqp = nesadapter->qp_table[le32_to_cpu(aeqe->aeqe_words
+ [NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_PRIV_OPERATION_DENIED event on QP%u,"
+ " nesqp = %p, AE reported %p\n",
+ nesqp->hwqp.qp_id, nesqp, *((struct nes_qp **)&context));
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_ACCESS_ERR;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ break;
+ case NES_AEQE_AEID_CQ_OPERATION_ERROR:
+ context <<= 1;
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_CQ_OPERATION_ERROR event on CQ%u, %p\n",
+ le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]), (void *)(unsigned long)context);
+ resource_allocated = nes_is_resource_allocated(nesadapter, nesadapter->allocated_cqs,
+ le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]));
+ if (resource_allocated) {
+ printk(KERN_ERR PFX "%s: Processing an NES_AEQE_AEID_CQ_OPERATION_ERROR event on CQ%u\n",
+ __FUNCTION__, le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]));
+ }
+ break;
+ case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER:
+ nesqp = nesadapter->qp_table[le32_to_cpu(
+ aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG"
+ "_FOR_AVAILABLE_BUFFER event on QP%u\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_ACCESS_ERR;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ /* tell cm to disconnect, cm will queue work to thread */
+ nes_add_ref(&nesqp->ibqp);
+ nes_cm_disconn(nesqp);
+ break;
+ case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE:
+ nesqp = *((struct nes_qp **)&context);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_DDP_UBE_INVALID_MSN"
+ "_NO_BUFFER_AVAILABLE event on QP%u\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_FATAL;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ /* tell cm to disconnect, cm will queue work to thread */
+ nes_add_ref(&nesqp->ibqp);
+ nes_cm_disconn(nesqp);
+ break;
+ case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR:
+ nesqp = *((struct nes_qp **)&context);
+ spin_lock_irqsave(&nesqp->lock, flags);
+ nesqp->hw_iwarp_state = iwarp_state;
+ nesqp->hw_tcp_state = tcp_state;
+ nesqp->last_aeq = async_event_id;
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR"
+ " event on QP%u \n Q2 Data:\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->ibqp.event_handler) {
+ ibevent.device = nesqp->ibqp.device;
+ ibevent.element.qp = &nesqp->ibqp;
+ ibevent.event = IB_EVENT_QP_FATAL;
+ nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+ }
+ /* tell cm to disconnect, cm will queue work to thread */
+ nes_add_ref(&nesqp->ibqp);
+ nes_cm_disconn(nesqp);
+ break;
+ /* TODO: additional AEs need to be here */
+ default:
+ nes_debug(NES_DBG_AEQ, "Processing an iWARP related AE for QP, misc = 0x%04X\n",
+ async_event_id);
+ break;
+ }
+
+}
+
+
+/**
+ * nes_iwarp_ce_handler
+ */
+void nes_iwarp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *hw_cq)
+{
+ struct nes_cq *nescq = container_of(hw_cq, struct nes_cq, hw_cq);
+
+ /* nes_debug(NES_DBG_CQ, "Processing completion event for iWARP CQ%u.\n",
+ nescq->hw_cq.cq_number); */
+ nes_write32(nesdev->regs+NES_CQ_ACK, nescq->hw_cq.cq_number);
+
+ if (nescq->ibcq.comp_handler)
+ nescq->ibcq.comp_handler(&nescq->ibcq, nescq->ibcq.cq_context);
+
+ return;
+}
+
+
+/**
+ * nes_manage_apbvt()
+ */
+int nes_manage_apbvt(struct nes_vnic *nesvnic, u32 accel_local_port,
+ u32 nic_index, u32 add_port)
+{
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ unsigned long flags;
+ struct nes_cqp_request *cqp_request;
+ int ret = 0;
+ u16 major_code;
+
+ /* Send manage APBVT request to CQP */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_QP, "Failed to get a cqp_request.\n");
+ return -ENOMEM;
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ nes_debug(NES_DBG_QP, "%s APBV for local port=%u(0x%04x), nic_index=%u\n",
+ (add_port == NES_MANAGE_APBVT_ADD) ? "ADD" : "DEL",
+ accel_local_port, accel_local_port, nic_index);
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, (NES_CQP_MANAGE_APBVT |
+ ((add_port == NES_MANAGE_APBVT_ADD) ? NES_CQP_APBVT_ADD : 0)));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ ((nic_index << NES_CQP_APBVT_NIC_SHIFT) | accel_local_port));
+
+ nes_debug(NES_DBG_QP, "Waiting for CQP completion for APBVT.\n");
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ if (add_port == NES_MANAGE_APBVT_ADD)
+ ret = wait_event_timeout(cqp_request->waitq, (cqp_request->request_done != 0),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_QP, "Completed, ret=%u, CQP Major:Minor codes = 0x%04X:0x%04X\n",
+ ret, cqp_request->major_code, cqp_request->minor_code);
+ major_code = cqp_request->major_code;
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ if (!ret)
+ return -ETIME;
+ else if (major_code)
+ return -EIO;
+ else
+ return 0;
+}
+
+
+/**
+ * nes_manage_arp_cache
+ */
+void nes_manage_arp_cache(struct net_device *netdev, unsigned char *mac_addr,
+ u32 ip_addr, u32 action)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev;
+ struct nes_cqp_request *cqp_request;
+ int arp_index;
+
+ nesdev = nesvnic->nesdev;
+ arp_index = nes_arp_table(nesdev, ip_addr, mac_addr, action);
+ if (arp_index == -1) {
+ return;
+ }
+
+ /* update the ARP entry */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_NETDEV, "Failed to get a cqp_request.\n");
+ return;
+ }
+ cqp_request->waiting = 0;
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] = cpu_to_le32(
+ NES_CQP_MANAGE_ARP_CACHE | NES_CQP_ARP_PERM);
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] |= cpu_to_le32(
+ (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_CQP_ARP_AEQ_INDEX_SHIFT);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(arp_index);
+
+ if (action == NES_ARP_ADD) {
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] |= cpu_to_le32(NES_CQP_ARP_VALID);
+ cqp_wqe->wqe_words[NES_CQP_ARP_WQE_MAC_ADDR_LOW_IDX] = cpu_to_le32(
+ (((u32)mac_addr[2]) << 24) | (((u32)mac_addr[3]) << 16) |
+ (((u32)mac_addr[4]) << 8) | (u32)mac_addr[5]);
+ cqp_wqe->wqe_words[NES_CQP_ARP_WQE_MAC_HIGH_IDX] = cpu_to_le32(
+ (((u32)mac_addr[0]) << 16) | (u32)mac_addr[1]);
+ } else {
+ cqp_wqe->wqe_words[NES_CQP_ARP_WQE_MAC_ADDR_LOW_IDX] = 0;
+ cqp_wqe->wqe_words[NES_CQP_ARP_WQE_MAC_HIGH_IDX] = 0;
+ }
+
+ nes_debug(NES_DBG_NETDEV, "Not waiting for CQP, cqp.sq_head=%u, cqp.sq_tail=%u\n",
+ nesdev->cqp.sq_head, nesdev->cqp.sq_tail);
+
+ atomic_set(&cqp_request->refcount, 1);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+}
+
+
+/**
+ * flush_wqes
+ */
+void flush_wqes(struct nes_device *nesdev, struct nes_qp *nesqp,
+ u32 which_wq, u32 wait_completion)
+{
+ unsigned long flags;
+ struct nes_cqp_request *cqp_request;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ int ret;
+
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_QP, "Failed to get a cqp_request.\n");
+ return;
+ }
+ if (wait_completion) {
+ cqp_request->waiting = 1;
+ atomic_set(&cqp_request->refcount, 2);
+ } else {
+ cqp_request->waiting = 0;
+ }
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
+ cpu_to_le32(NES_CQP_FLUSH_WQES | which_wq);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(nesqp->hwqp.qp_id);
+
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ if (wait_completion) {
+ /* Wait for CQP */
+ ret = wait_event_timeout(cqp_request->waitq, (cqp_request->request_done != 0),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_QP, "Flush SQ QP WQEs completed, ret=%u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X\n",
+ ret, cqp_request->major_code, cqp_request->minor_code);
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+*
+* This software is available to you under a choice of one of two
+* licenses. You may choose to be licensed under the terms of the GNU
+* General Public License (GPL) Version 2, available from the file
+* COPYING in the main directory of this source tree, or the
+* OpenIB.org BSD license below:
+*
+* Redistribution and use in source and binary forms, with or
+* without modification, are permitted provided that the following
+* conditions are met:
+*
+* - Redistributions of source code must retain the above
+* copyright notice, this list of conditions and the following
+* disclaimer.
+*
+* - 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.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+
+#ifndef __NES_HW_H
+#define __NES_HW_H
+
+#define NES_PHY_TYPE_1G 2
+#define NES_PHY_TYPE_IRIS 3
+#define NES_PHY_TYPE_PUMA_10G 6
+
+#define NES_MULTICAST_PF_MAX 8
+
+enum pci_regs {
+ NES_INT_STAT = 0x0000,
+ NES_INT_MASK = 0x0004,
+ NES_INT_PENDING = 0x0008,
+ NES_INTF_INT_STAT = 0x000C,
+ NES_INTF_INT_MASK = 0x0010,
+ NES_TIMER_STAT = 0x0014,
+ NES_PERIODIC_CONTROL = 0x0018,
+ NES_ONE_SHOT_CONTROL = 0x001C,
+ NES_EEPROM_COMMAND = 0x0020,
+ NES_EEPROM_DATA = 0x0024,
+ NES_FLASH_COMMAND = 0x0028,
+ NES_FLASH_DATA = 0x002C,
+ NES_SOFTWARE_RESET = 0x0030,
+ NES_CQ_ACK = 0x0034,
+ NES_WQE_ALLOC = 0x0040,
+ NES_CQE_ALLOC = 0x0044,
+};
+
+enum indexed_regs {
+ NES_IDX_CREATE_CQP_LOW = 0x0000,
+ NES_IDX_CREATE_CQP_HIGH = 0x0004,
+ NES_IDX_QP_CONTROL = 0x0040,
+ NES_IDX_FLM_CONTROL = 0x0080,
+ NES_IDX_INT_CPU_STATUS = 0x00a0,
+ NES_IDX_GPIO_CONTROL = 0x00f0,
+ NES_IDX_GPIO_DATA = 0x00f4,
+ NES_IDX_TCP_CONFIG0 = 0x01e4,
+ NES_IDX_TCP_TIMER_CONFIG = 0x01ec,
+ NES_IDX_TCP_NOW = 0x01f0,
+ NES_IDX_QP_MAX_CFG_SIZES = 0x0200,
+ NES_IDX_QP_CTX_SIZE = 0x0218,
+ NES_IDX_TCP_TIMER_SIZE0 = 0x0238,
+ NES_IDX_TCP_TIMER_SIZE1 = 0x0240,
+ NES_IDX_ARP_CACHE_SIZE = 0x0258,
+ NES_IDX_CQ_CTX_SIZE = 0x0260,
+ NES_IDX_MRT_SIZE = 0x0278,
+ NES_IDX_PBL_REGION_SIZE = 0x0280,
+ NES_IDX_IRRQ_COUNT = 0x02b0,
+ NES_IDX_RX_WINDOW_BUFFER_PAGE_TABLE_SIZE = 0x02f0,
+ NES_IDX_RX_WINDOW_BUFFER_SIZE = 0x0300,
+ NES_IDX_DST_IP_ADDR = 0x0400,
+ NES_IDX_PCIX_DIAG = 0x08e8,
+ NES_IDX_MPP_DEBUG = 0x0a00,
+ NES_IDX_PORT_RX_DISCARDS = 0x0a30,
+ NES_IDX_PORT_TX_DISCARDS = 0x0a34,
+ NES_IDX_MPP_LB_DEBUG = 0x0b00,
+ NES_IDX_DENALI_CTL_22 = 0x1058,
+ NES_IDX_MAC_TX_CONTROL = 0x2000,
+ NES_IDX_MAC_TX_CONFIG = 0x2004,
+ NES_IDX_MAC_TX_PAUSE_QUANTA = 0x2008,
+ NES_IDX_MAC_RX_CONTROL = 0x200c,
+ NES_IDX_MAC_RX_CONFIG = 0x2010,
+ NES_IDX_MAC_EXACT_MATCH_BOTTOM = 0x201c,
+ NES_IDX_MAC_MDIO_CONTROL = 0x2084,
+ NES_IDX_MAC_TX_OCTETS_LOW = 0x2100,
+ NES_IDX_MAC_TX_OCTETS_HIGH = 0x2104,
+ NES_IDX_MAC_TX_FRAMES_LOW = 0x2108,
+ NES_IDX_MAC_TX_FRAMES_HIGH = 0x210c,
+ NES_IDX_MAC_TX_PAUSE_FRAMES = 0x2118,
+ NES_IDX_MAC_TX_ERRORS = 0x2138,
+ NES_IDX_MAC_RX_OCTETS_LOW = 0x213c,
+ NES_IDX_MAC_RX_OCTETS_HIGH = 0x2140,
+ NES_IDX_MAC_RX_FRAMES_LOW = 0x2144,
+ NES_IDX_MAC_RX_FRAMES_HIGH = 0x2148,
+ NES_IDX_MAC_RX_BC_FRAMES_LOW = 0x214c,
+ NES_IDX_MAC_RX_MC_FRAMES_HIGH = 0x2150,
+ NES_IDX_MAC_RX_PAUSE_FRAMES = 0x2154,
+ NES_IDX_MAC_RX_SHORT_FRAMES = 0x2174,
+ NES_IDX_MAC_RX_OVERSIZED_FRAMES = 0x2178,
+ NES_IDX_MAC_RX_JABBER_FRAMES = 0x217c,
+ NES_IDX_MAC_RX_CRC_ERR_FRAMES = 0x2180,
+ NES_IDX_MAC_RX_LENGTH_ERR_FRAMES = 0x2184,
+ NES_IDX_MAC_RX_SYMBOL_ERR_FRAMES = 0x2188,
+ NES_IDX_MAC_INT_STATUS = 0x21f0,
+ NES_IDX_MAC_INT_MASK = 0x21f4,
+ NES_IDX_PHY_PCS_CONTROL_STATUS0 = 0x2800,
+ NES_IDX_PHY_PCS_CONTROL_STATUS1 = 0x2a00,
+ NES_IDX_ETH_SERDES_COMMON_CONTROL0 = 0x2808,
+ NES_IDX_ETH_SERDES_COMMON_CONTROL1 = 0x2a08,
+ NES_IDX_ETH_SERDES_COMMON_STATUS0 = 0x280c,
+ NES_IDX_ETH_SERDES_COMMON_STATUS1 = 0x2a0c,
+ NES_IDX_ETH_SERDES_TX_EMP0 = 0x2810,
+ NES_IDX_ETH_SERDES_TX_EMP1 = 0x2a10,
+ NES_IDX_ETH_SERDES_TX_DRIVE0 = 0x2814,
+ NES_IDX_ETH_SERDES_TX_DRIVE1 = 0x2a14,
+ NES_IDX_ETH_SERDES_RX_MODE0 = 0x2818,
+ NES_IDX_ETH_SERDES_RX_MODE1 = 0x2a18,
+ NES_IDX_ETH_SERDES_RX_SIGDET0 = 0x281c,
+ NES_IDX_ETH_SERDES_RX_SIGDET1 = 0x2a1c,
+ NES_IDX_ETH_SERDES_BYPASS0 = 0x2820,
+ NES_IDX_ETH_SERDES_BYPASS1 = 0x2a20,
+ NES_IDX_ETH_SERDES_LOOPBACK_CONTROL0 = 0x2824,
+ NES_IDX_ETH_SERDES_LOOPBACK_CONTROL1 = 0x2a24,
+ NES_IDX_ETH_SERDES_RX_EQ_CONTROL0 = 0x2828,
+ NES_IDX_ETH_SERDES_RX_EQ_CONTROL1 = 0x2a28,
+ NES_IDX_ETH_SERDES_RX_EQ_STATUS0 = 0x282c,
+ NES_IDX_ETH_SERDES_RX_EQ_STATUS1 = 0x2a2c,
+ NES_IDX_ETH_SERDES_CDR_RESET0 = 0x2830,
+ NES_IDX_ETH_SERDES_CDR_RESET1 = 0x2a30,
+ NES_IDX_ETH_SERDES_CDR_CONTROL0 = 0x2834,
+ NES_IDX_ETH_SERDES_CDR_CONTROL1 = 0x2a34,
+ NES_IDX_ETH_SERDES_TX_HIGHZ_LANE_MODE0 = 0x2838,
+ NES_IDX_ETH_SERDES_TX_HIGHZ_LANE_MODE1 = 0x2a38,
+ NES_IDX_ENDNODE0_NSTAT_RX_DISCARD = 0x3080,
+ NES_IDX_ENDNODE0_NSTAT_RX_OCTETS_LO = 0x3000,
+ NES_IDX_ENDNODE0_NSTAT_RX_OCTETS_HI = 0x3004,
+ NES_IDX_ENDNODE0_NSTAT_RX_FRAMES_LO = 0x3008,
+ NES_IDX_ENDNODE0_NSTAT_RX_FRAMES_HI = 0x300c,
+ NES_IDX_ENDNODE0_NSTAT_TX_OCTETS_LO = 0x7000,
+ NES_IDX_ENDNODE0_NSTAT_TX_OCTETS_HI = 0x7004,
+ NES_IDX_ENDNODE0_NSTAT_TX_FRAMES_LO = 0x7008,
+ NES_IDX_ENDNODE0_NSTAT_TX_FRAMES_HI = 0x700c,
+ NES_IDX_CM_CONFIG = 0x5100,
+ NES_IDX_NIC_LOGPORT_TO_PHYPORT = 0x6000,
+ NES_IDX_NIC_PHYPORT_TO_USW = 0x6008,
+ NES_IDX_NIC_ACTIVE = 0x6010,
+ NES_IDX_NIC_UNICAST_ALL = 0x6018,
+ NES_IDX_NIC_MULTICAST_ALL = 0x6020,
+ NES_IDX_NIC_MULTICAST_ENABLE = 0x6028,
+ NES_IDX_NIC_BROADCAST_ON = 0x6030,
+ NES_IDX_USED_CHUNKS_TX = 0x60b0,
+ NES_IDX_TX_POOL_SIZE = 0x60b8,
+ NES_IDX_QUAD_HASH_TABLE_SIZE = 0x6148,
+ NES_IDX_PERFECT_FILTER_LOW = 0x6200,
+ NES_IDX_PERFECT_FILTER_HIGH = 0x6204,
+ NES_IDX_IPV4_TCP_REXMITS = 0x7080,
+ NES_IDX_DEBUG_ERROR_CONTROL_STATUS = 0x913c,
+ NES_IDX_DEBUG_ERROR_MASKS0 = 0x9140,
+ NES_IDX_DEBUG_ERROR_MASKS1 = 0x9144,
+ NES_IDX_DEBUG_ERROR_MASKS2 = 0x9148,
+ NES_IDX_DEBUG_ERROR_MASKS3 = 0x914c,
+ NES_IDX_DEBUG_ERROR_MASKS4 = 0x9150,
+ NES_IDX_DEBUG_ERROR_MASKS5 = 0x9154,
+};
+
+#define NES_IDX_MAC_TX_CONFIG_ENABLE_PAUSE 1
+#define NES_IDX_MPP_DEBUG_PORT_DISABLE_PAUSE (1 << 17)
+
+enum nes_cqp_opcodes {
+ NES_CQP_CREATE_QP = 0x00,
+ NES_CQP_MODIFY_QP = 0x01,
+ NES_CQP_DESTROY_QP = 0x02,
+ NES_CQP_CREATE_CQ = 0x03,
+ NES_CQP_MODIFY_CQ = 0x04,
+ NES_CQP_DESTROY_CQ = 0x05,
+ NES_CQP_ALLOCATE_STAG = 0x09,
+ NES_CQP_REGISTER_STAG = 0x0a,
+ NES_CQP_QUERY_STAG = 0x0b,
+ NES_CQP_REGISTER_SHARED_STAG = 0x0c,
+ NES_CQP_DEALLOCATE_STAG = 0x0d,
+ NES_CQP_MANAGE_ARP_CACHE = 0x0f,
+ NES_CQP_SUSPEND_QPS = 0x11,
+ NES_CQP_UPLOAD_CONTEXT = 0x13,
+ NES_CQP_CREATE_CEQ = 0x16,
+ NES_CQP_DESTROY_CEQ = 0x18,
+ NES_CQP_CREATE_AEQ = 0x19,
+ NES_CQP_DESTROY_AEQ = 0x1b,
+ NES_CQP_LMI_ACCESS = 0x20,
+ NES_CQP_FLUSH_WQES = 0x22,
+ NES_CQP_MANAGE_APBVT = 0x23
+};
+
+enum nes_cqp_wqe_word_idx {
+ NES_CQP_WQE_OPCODE_IDX = 0,
+ NES_CQP_WQE_ID_IDX = 1,
+ NES_CQP_WQE_COMP_CTX_LOW_IDX = 2,
+ NES_CQP_WQE_COMP_CTX_HIGH_IDX = 3,
+ NES_CQP_WQE_COMP_SCRATCH_LOW_IDX = 4,
+ NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX = 5,
+};
+
+enum nes_cqp_cq_wqeword_idx {
+ NES_CQP_CQ_WQE_PBL_LOW_IDX = 6,
+ NES_CQP_CQ_WQE_PBL_HIGH_IDX = 7,
+ NES_CQP_CQ_WQE_CQ_CONTEXT_LOW_IDX = 8,
+ NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX = 9,
+ NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX = 10,
+};
+
+enum nes_cqp_stag_wqeword_idx {
+ NES_CQP_STAG_WQE_PBL_BLK_COUNT_IDX = 1,
+ NES_CQP_STAG_WQE_LEN_HIGH_PD_IDX = 6,
+ NES_CQP_STAG_WQE_LEN_LOW_IDX = 7,
+ NES_CQP_STAG_WQE_STAG_IDX = 8,
+ NES_CQP_STAG_WQE_VA_LOW_IDX = 10,
+ NES_CQP_STAG_WQE_VA_HIGH_IDX = 11,
+ NES_CQP_STAG_WQE_PA_LOW_IDX = 12,
+ NES_CQP_STAG_WQE_PA_HIGH_IDX = 13,
+ NES_CQP_STAG_WQE_PBL_LEN_IDX = 14
+};
+
+#define NES_CQP_OP_IWARP_STATE_SHIFT 28
+
+enum nes_cqp_qp_bits {
+ NES_CQP_QP_ARP_VALID = (1<<8),
+ NES_CQP_QP_WINBUF_VALID = (1<<9),
+ NES_CQP_QP_CONTEXT_VALID = (1<<10),
+ NES_CQP_QP_ORD_VALID = (1<<11),
+ NES_CQP_QP_WINBUF_DATAIND_EN = (1<<12),
+ NES_CQP_QP_VIRT_WQS = (1<<13),
+ NES_CQP_QP_DEL_HTE = (1<<14),
+ NES_CQP_QP_CQS_VALID = (1<<15),
+ NES_CQP_QP_TYPE_TSA = 0,
+ NES_CQP_QP_TYPE_IWARP = (1<<16),
+ NES_CQP_QP_TYPE_CQP = (4<<16),
+ NES_CQP_QP_TYPE_NIC = (5<<16),
+ NES_CQP_QP_MSS_CHG = (1<<20),
+ NES_CQP_QP_STATIC_RESOURCES = (1<<21),
+ NES_CQP_QP_IGNORE_MW_BOUND = (1<<22),
+ NES_CQP_QP_VWQ_USE_LMI = (1<<23),
+ NES_CQP_QP_IWARP_STATE_IDLE = (1<<NES_CQP_OP_IWARP_STATE_SHIFT),
+ NES_CQP_QP_IWARP_STATE_RTS = (2<<NES_CQP_OP_IWARP_STATE_SHIFT),
+ NES_CQP_QP_IWARP_STATE_CLOSING = (3<<NES_CQP_OP_IWARP_STATE_SHIFT),
+ NES_CQP_QP_IWARP_STATE_TERMINATE = (5<<NES_CQP_OP_IWARP_STATE_SHIFT),
+ NES_CQP_QP_IWARP_STATE_ERROR = (6<<NES_CQP_OP_IWARP_STATE_SHIFT),
+ NES_CQP_QP_IWARP_STATE_MASK = (7<<NES_CQP_OP_IWARP_STATE_SHIFT),
+ NES_CQP_QP_RESET = (1<<31),
+};
+
+enum nes_cqp_qp_wqe_word_idx {
+ NES_CQP_QP_WQE_CONTEXT_LOW_IDX = 6,
+ NES_CQP_QP_WQE_CONTEXT_HIGH_IDX = 7,
+ NES_CQP_QP_WQE_NEW_MSS_IDX = 15,
+};
+
+enum nes_nic_ctx_bits {
+ NES_NIC_CTX_RQ_SIZE_32 = (3<<8),
+ NES_NIC_CTX_RQ_SIZE_512 = (3<<8),
+ NES_NIC_CTX_SQ_SIZE_32 = (1<<10),
+ NES_NIC_CTX_SQ_SIZE_512 = (3<<10),
+};
+
+enum nes_nic_qp_ctx_word_idx {
+ NES_NIC_CTX_MISC_IDX = 0,
+ NES_NIC_CTX_SQ_LOW_IDX = 2,
+ NES_NIC_CTX_SQ_HIGH_IDX = 3,
+ NES_NIC_CTX_RQ_LOW_IDX = 4,
+ NES_NIC_CTX_RQ_HIGH_IDX = 5,
+};
+
+enum nes_cqp_cq_bits {
+ NES_CQP_CQ_CEQE_MASK = (1<<9),
+ NES_CQP_CQ_CEQ_VALID = (1<<10),
+ NES_CQP_CQ_RESIZE = (1<<11),
+ NES_CQP_CQ_CHK_OVERFLOW = (1<<12),
+ NES_CQP_CQ_4KB_CHUNK = (1<<14),
+ NES_CQP_CQ_VIRT = (1<<15),
+};
+
+enum nes_cqp_stag_bits {
+ NES_CQP_STAG_VA_TO = (1<<9),
+ NES_CQP_STAG_DEALLOC_PBLS = (1<<10),
+ NES_CQP_STAG_PBL_BLK_SIZE = (1<<11),
+ NES_CQP_STAG_MR = (1<<13),
+ NES_CQP_STAG_RIGHTS_LOCAL_READ = (1<<16),
+ NES_CQP_STAG_RIGHTS_LOCAL_WRITE = (1<<17),
+ NES_CQP_STAG_RIGHTS_REMOTE_READ = (1<<18),
+ NES_CQP_STAG_RIGHTS_REMOTE_WRITE = (1<<19),
+ NES_CQP_STAG_RIGHTS_WINDOW_BIND = (1<<20),
+ NES_CQP_STAG_REM_ACC_EN = (1<<21),
+ NES_CQP_STAG_LEAVE_PENDING = (1<<31),
+};
+
+enum nes_cqp_ceq_wqeword_idx {
+ NES_CQP_CEQ_WQE_ELEMENT_COUNT_IDX = 1,
+ NES_CQP_CEQ_WQE_PBL_LOW_IDX = 6,
+ NES_CQP_CEQ_WQE_PBL_HIGH_IDX = 7,
+};
+
+enum nes_cqp_ceq_bits {
+ NES_CQP_CEQ_4KB_CHUNK = (1<<14),
+ NES_CQP_CEQ_VIRT = (1<<15),
+};
+
+enum nes_cqp_aeq_wqeword_idx {
+ NES_CQP_AEQ_WQE_ELEMENT_COUNT_IDX = 1,
+ NES_CQP_AEQ_WQE_PBL_LOW_IDX = 6,
+ NES_CQP_AEQ_WQE_PBL_HIGH_IDX = 7,
+};
+
+enum nes_cqp_aeq_bits {
+ NES_CQP_AEQ_4KB_CHUNK = (1<<14),
+ NES_CQP_AEQ_VIRT = (1<<15),
+};
+
+enum nes_cqp_lmi_wqeword_idx {
+ NES_CQP_LMI_WQE_LMI_OFFSET_IDX = 1,
+ NES_CQP_LMI_WQE_FRAG_LOW_IDX = 8,
+ NES_CQP_LMI_WQE_FRAG_HIGH_IDX = 9,
+ NES_CQP_LMI_WQE_FRAG_LEN_IDX = 10,
+};
+
+enum nes_cqp_arp_wqeword_idx {
+ NES_CQP_ARP_WQE_MAC_ADDR_LOW_IDX = 6,
+ NES_CQP_ARP_WQE_MAC_HIGH_IDX = 7,
+ NES_CQP_ARP_WQE_REACHABILITY_MAX_IDX = 1,
+};
+
+enum nes_cqp_upload_wqeword_idx {
+ NES_CQP_UPLOAD_WQE_CTXT_LOW_IDX = 6,
+ NES_CQP_UPLOAD_WQE_CTXT_HIGH_IDX = 7,
+ NES_CQP_UPLOAD_WQE_HTE_IDX = 8,
+};
+
+enum nes_cqp_arp_bits {
+ NES_CQP_ARP_VALID = (1<<8),
+ NES_CQP_ARP_PERM = (1<<9),
+};
+
+enum nes_cqp_flush_bits {
+ NES_CQP_FLUSH_SQ = (1<<30),
+ NES_CQP_FLUSH_RQ = (1<<31),
+};
+
+enum nes_cqe_opcode_bits {
+ NES_CQE_STAG_VALID = (1<<6),
+ NES_CQE_ERROR = (1<<7),
+ NES_CQE_SQ = (1<<8),
+ NES_CQE_SE = (1<<9),
+ NES_CQE_PSH = (1<<29),
+ NES_CQE_FIN = (1<<30),
+ NES_CQE_VALID = (1<<31),
+};
+
+
+enum nes_cqe_word_idx {
+ NES_CQE_PAYLOAD_LENGTH_IDX = 0,
+ NES_CQE_COMP_COMP_CTX_LOW_IDX = 2,
+ NES_CQE_COMP_COMP_CTX_HIGH_IDX = 3,
+ NES_CQE_INV_STAG_IDX = 4,
+ NES_CQE_QP_ID_IDX = 5,
+ NES_CQE_ERROR_CODE_IDX = 6,
+ NES_CQE_OPCODE_IDX = 7,
+};
+
+enum nes_ceqe_word_idx {
+ NES_CEQE_CQ_CTX_LOW_IDX = 0,
+ NES_CEQE_CQ_CTX_HIGH_IDX = 1,
+};
+
+enum nes_ceqe_status_bit {
+ NES_CEQE_VALID = (1<<31),
+};
+
+enum nes_int_bits {
+ NES_INT_CEQ0 = (1<<0),
+ NES_INT_CEQ1 = (1<<1),
+ NES_INT_CEQ2 = (1<<2),
+ NES_INT_CEQ3 = (1<<3),
+ NES_INT_CEQ4 = (1<<4),
+ NES_INT_CEQ5 = (1<<5),
+ NES_INT_CEQ6 = (1<<6),
+ NES_INT_CEQ7 = (1<<7),
+ NES_INT_CEQ8 = (1<<8),
+ NES_INT_CEQ9 = (1<<9),
+ NES_INT_CEQ10 = (1<<10),
+ NES_INT_CEQ11 = (1<<11),
+ NES_INT_CEQ12 = (1<<12),
+ NES_INT_CEQ13 = (1<<13),
+ NES_INT_CEQ14 = (1<<14),
+ NES_INT_CEQ15 = (1<<15),
+ NES_INT_AEQ0 = (1<<16),
+ NES_INT_AEQ1 = (1<<17),
+ NES_INT_AEQ2 = (1<<18),
+ NES_INT_AEQ3 = (1<<19),
+ NES_INT_AEQ4 = (1<<20),
+ NES_INT_AEQ5 = (1<<21),
+ NES_INT_AEQ6 = (1<<22),
+ NES_INT_AEQ7 = (1<<23),
+ NES_INT_MAC0 = (1<<24),
+ NES_INT_MAC1 = (1<<25),
+ NES_INT_MAC2 = (1<<26),
+ NES_INT_MAC3 = (1<<27),
+ NES_INT_TSW = (1<<28),
+ NES_INT_TIMER = (1<<29),
+ NES_INT_INTF = (1<<30),
+};
+
+enum nes_intf_int_bits {
+ NES_INTF_INT_PCIERR = (1<<0),
+ NES_INTF_PERIODIC_TIMER = (1<<2),
+ NES_INTF_ONE_SHOT_TIMER = (1<<3),
+ NES_INTF_INT_CRITERR = (1<<14),
+ NES_INTF_INT_AEQ0_OFLOW = (1<<16),
+ NES_INTF_INT_AEQ1_OFLOW = (1<<17),
+ NES_INTF_INT_AEQ2_OFLOW = (1<<18),
+ NES_INTF_INT_AEQ3_OFLOW = (1<<19),
+ NES_INTF_INT_AEQ4_OFLOW = (1<<20),
+ NES_INTF_INT_AEQ5_OFLOW = (1<<21),
+ NES_INTF_INT_AEQ6_OFLOW = (1<<22),
+ NES_INTF_INT_AEQ7_OFLOW = (1<<23),
+ NES_INTF_INT_AEQ_OFLOW = (0xff<<16),
+};
+
+enum nes_mac_int_bits {
+ NES_MAC_INT_LINK_STAT_CHG = (1<<1),
+ NES_MAC_INT_XGMII_EXT = (1<<2),
+ NES_MAC_INT_TX_UNDERFLOW = (1<<6),
+ NES_MAC_INT_TX_ERROR = (1<<7),
+};
+
+enum nes_cqe_allocate_bits {
+ NES_CQE_ALLOC_INC_SELECT = (1<<28),
+ NES_CQE_ALLOC_NOTIFY_NEXT = (1<<29),
+ NES_CQE_ALLOC_NOTIFY_SE = (1<<30),
+ NES_CQE_ALLOC_RESET = (1<<31),
+};
+
+enum nes_nic_rq_wqe_word_idx {
+ NES_NIC_RQ_WQE_LENGTH_1_0_IDX = 0,
+ NES_NIC_RQ_WQE_LENGTH_3_2_IDX = 1,
+ NES_NIC_RQ_WQE_FRAG0_LOW_IDX = 2,
+ NES_NIC_RQ_WQE_FRAG0_HIGH_IDX = 3,
+ NES_NIC_RQ_WQE_FRAG1_LOW_IDX = 4,
+ NES_NIC_RQ_WQE_FRAG1_HIGH_IDX = 5,
+ NES_NIC_RQ_WQE_FRAG2_LOW_IDX = 6,
+ NES_NIC_RQ_WQE_FRAG2_HIGH_IDX = 7,
+ NES_NIC_RQ_WQE_FRAG3_LOW_IDX = 8,
+ NES_NIC_RQ_WQE_FRAG3_HIGH_IDX = 9,
+};
+
+enum nes_nic_sq_wqe_word_idx {
+ NES_NIC_SQ_WQE_MISC_IDX = 0,
+ NES_NIC_SQ_WQE_TOTAL_LENGTH_IDX = 1,
+ NES_NIC_SQ_WQE_LSO_INFO_IDX = 2,
+ NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX = 3,
+ NES_NIC_SQ_WQE_LENGTH_2_1_IDX = 4,
+ NES_NIC_SQ_WQE_LENGTH_4_3_IDX = 5,
+ NES_NIC_SQ_WQE_FRAG0_LOW_IDX = 6,
+ NES_NIC_SQ_WQE_FRAG0_HIGH_IDX = 7,
+ NES_NIC_SQ_WQE_FRAG1_LOW_IDX = 8,
+ NES_NIC_SQ_WQE_FRAG1_HIGH_IDX = 9,
+ NES_NIC_SQ_WQE_FRAG2_LOW_IDX = 10,
+ NES_NIC_SQ_WQE_FRAG2_HIGH_IDX = 11,
+ NES_NIC_SQ_WQE_FRAG3_LOW_IDX = 12,
+ NES_NIC_SQ_WQE_FRAG3_HIGH_IDX = 13,
+ NES_NIC_SQ_WQE_FRAG4_LOW_IDX = 14,
+ NES_NIC_SQ_WQE_FRAG4_HIGH_IDX = 15,
+};
+
+enum nes_iwarp_sq_wqe_word_idx {
+ NES_IWARP_SQ_WQE_MISC_IDX = 0,
+ NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX = 1,
+ NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX = 2,
+ NES_IWARP_SQ_WQE_COMP_CTX_HIGH_IDX = 3,
+ NES_IWARP_SQ_WQE_COMP_SCRATCH_LOW_IDX = 4,
+ NES_IWARP_SQ_WQE_COMP_SCRATCH_HIGH_IDX = 5,
+ NES_IWARP_SQ_WQE_INV_STAG_LOW_IDX = 7,
+ NES_IWARP_SQ_WQE_RDMA_TO_LOW_IDX = 8,
+ NES_IWARP_SQ_WQE_RDMA_TO_HIGH_IDX = 9,
+ NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX = 10,
+ NES_IWARP_SQ_WQE_RDMA_STAG_IDX = 11,
+ NES_IWARP_SQ_WQE_IMM_DATA_START_IDX = 12,
+ NES_IWARP_SQ_WQE_FRAG0_LOW_IDX = 16,
+ NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX = 17,
+ NES_IWARP_SQ_WQE_LENGTH0_IDX = 18,
+ NES_IWARP_SQ_WQE_STAG0_IDX = 19,
+ NES_IWARP_SQ_WQE_FRAG1_LOW_IDX = 20,
+ NES_IWARP_SQ_WQE_FRAG1_HIGH_IDX = 21,
+ NES_IWARP_SQ_WQE_LENGTH1_IDX = 22,
+ NES_IWARP_SQ_WQE_STAG1_IDX = 23,
+ NES_IWARP_SQ_WQE_FRAG2_LOW_IDX = 24,
+ NES_IWARP_SQ_WQE_FRAG2_HIGH_IDX = 25,
+ NES_IWARP_SQ_WQE_LENGTH2_IDX = 26,
+ NES_IWARP_SQ_WQE_STAG2_IDX = 27,
+ NES_IWARP_SQ_WQE_FRAG3_LOW_IDX = 28,
+ NES_IWARP_SQ_WQE_FRAG3_HIGH_IDX = 29,
+ NES_IWARP_SQ_WQE_LENGTH3_IDX = 30,
+ NES_IWARP_SQ_WQE_STAG3_IDX = 31,
+};
+
+enum nes_iwarp_sq_bind_wqe_word_idx {
+ NES_IWARP_SQ_BIND_WQE_MR_IDX = 6,
+ NES_IWARP_SQ_BIND_WQE_MW_IDX = 7,
+ NES_IWARP_SQ_BIND_WQE_LENGTH_LOW_IDX = 8,
+ NES_IWARP_SQ_BIND_WQE_LENGTH_HIGH_IDX = 9,
+ NES_IWARP_SQ_BIND_WQE_VA_FBO_LOW_IDX = 10,
+ NES_IWARP_SQ_BIND_WQE_VA_FBO_HIGH_IDX = 11,
+};
+
+enum nes_iwarp_sq_fmr_wqe_word_idx {
+ NES_IWARP_SQ_FMR_WQE_MR_STAG_IDX = 7,
+ NES_IWARP_SQ_FMR_WQE_LENGTH_LOW_IDX = 8,
+ NES_IWARP_SQ_FMR_WQE_LENGTH_HIGH_IDX = 9,
+ NES_IWARP_SQ_FMR_WQE_VA_FBO_LOW_IDX = 10,
+ NES_IWARP_SQ_FMR_WQE_VA_FBO_HIGH_IDX = 11,
+ NES_IWARP_SQ_FMR_WQE_PBL_ADDR_LOW_IDX = 12,
+ NES_IWARP_SQ_FMR_WQE_PBL_ADDR_HIGH_IDX = 13,
+ NES_IWARP_SQ_FMR_WQE_PBL_LENGTH_IDX = 14,
+};
+
+enum nes_iwarp_sq_locinv_wqe_word_idx {
+ NES_IWARP_SQ_LOCINV_WQE_INV_STAG_IDX = 6,
+};
+
+
+enum nes_iwarp_rq_wqe_word_idx {
+ NES_IWARP_RQ_WQE_TOTAL_PAYLOAD_IDX = 1,
+ NES_IWARP_RQ_WQE_COMP_CTX_LOW_IDX = 2,
+ NES_IWARP_RQ_WQE_COMP_CTX_HIGH_IDX = 3,
+ NES_IWARP_RQ_WQE_COMP_SCRATCH_LOW_IDX = 4,
+ NES_IWARP_RQ_WQE_COMP_SCRATCH_HIGH_IDX = 5,
+ NES_IWARP_RQ_WQE_FRAG0_LOW_IDX = 8,
+ NES_IWARP_RQ_WQE_FRAG0_HIGH_IDX = 9,
+ NES_IWARP_RQ_WQE_LENGTH0_IDX = 10,
+ NES_IWARP_RQ_WQE_STAG0_IDX = 11,
+ NES_IWARP_RQ_WQE_FRAG1_LOW_IDX = 12,
+ NES_IWARP_RQ_WQE_FRAG1_HIGH_IDX = 13,
+ NES_IWARP_RQ_WQE_LENGTH1_IDX = 14,
+ NES_IWARP_RQ_WQE_STAG1_IDX = 15,
+ NES_IWARP_RQ_WQE_FRAG2_LOW_IDX = 16,
+ NES_IWARP_RQ_WQE_FRAG2_HIGH_IDX = 17,
+ NES_IWARP_RQ_WQE_LENGTH2_IDX = 18,
+ NES_IWARP_RQ_WQE_STAG2_IDX = 19,
+ NES_IWARP_RQ_WQE_FRAG3_LOW_IDX = 20,
+ NES_IWARP_RQ_WQE_FRAG3_HIGH_IDX = 21,
+ NES_IWARP_RQ_WQE_LENGTH3_IDX = 22,
+ NES_IWARP_RQ_WQE_STAG3_IDX = 23,
+};
+
+enum nes_nic_sq_wqe_bits {
+ NES_NIC_SQ_WQE_PHDR_CS_READY = (1<<21),
+ NES_NIC_SQ_WQE_LSO_ENABLE = (1<<22),
+ NES_NIC_SQ_WQE_TAGVALUE_ENABLE = (1<<23),
+ NES_NIC_SQ_WQE_DISABLE_CHKSUM = (1<<30),
+ NES_NIC_SQ_WQE_COMPLETION = (1<<31),
+};
+
+enum nes_nic_cqe_word_idx {
+ NES_NIC_CQE_ACCQP_ID_IDX = 0,
+ NES_NIC_CQE_TAG_PKT_TYPE_IDX = 2,
+ NES_NIC_CQE_MISC_IDX = 3,
+};
+
+#define NES_PKT_TYPE_APBVT_BITS 0xC112
+#define NES_PKT_TYPE_APBVT_MASK 0xff3e
+
+#define NES_PKT_TYPE_PVALID_BITS 0x10000000
+#define NES_PKT_TYPE_PVALID_MASK 0x30000000
+
+#define NES_PKT_TYPE_TCPV4_BITS 0x0110
+#define NES_PKT_TYPE_TCPV4_MASK 0x3f30
+
+#define NES_PKT_TYPE_UDPV4_BITS 0x0210
+#define NES_PKT_TYPE_UDPV4_MASK 0x3f30
+
+#define NES_PKT_TYPE_IPV4_BITS 0x0010
+#define NES_PKT_TYPE_IPV4_MASK 0x3f30
+
+#define NES_PKT_TYPE_OTHER_BITS 0x0000
+#define NES_PKT_TYPE_OTHER_MASK 0x0030
+
+#define NES_NIC_CQE_ERRV_SHIFT 16
+enum nes_nic_ev_bits {
+ NES_NIC_ERRV_BITS_MODE = (1<<0),
+ NES_NIC_ERRV_BITS_IPV4_CSUM_ERR = (1<<1),
+ NES_NIC_ERRV_BITS_TCPUDP_CSUM_ERR = (1<<2),
+ NES_NIC_ERRV_BITS_WQE_OVERRUN = (1<<3),
+ NES_NIC_ERRV_BITS_IPH_ERR = (1<<4),
+};
+
+enum nes_nic_cqe_bits {
+ NES_NIC_CQE_ERRV_MASK = (0xff<<NES_NIC_CQE_ERRV_SHIFT),
+ NES_NIC_CQE_SQ = (1<<24),
+ NES_NIC_CQE_ACCQP_PORT = (1<<28),
+ NES_NIC_CQE_ACCQP_VALID = (1<<29),
+ NES_NIC_CQE_TAG_VALID = (1<<30),
+ NES_NIC_CQE_VALID = (1<<31),
+};
+
+enum nes_aeqe_word_idx {
+ NES_AEQE_COMP_CTXT_LOW_IDX = 0,
+ NES_AEQE_COMP_CTXT_HIGH_IDX = 1,
+ NES_AEQE_COMP_QP_CQ_ID_IDX = 2,
+ NES_AEQE_MISC_IDX = 3,
+};
+
+enum nes_aeqe_bits {
+ NES_AEQE_QP = (1<<16),
+ NES_AEQE_CQ = (1<<17),
+ NES_AEQE_SQ = (1<<18),
+ NES_AEQE_INBOUND_RDMA = (1<<19),
+ NES_AEQE_IWARP_STATE_MASK = (7<<20),
+ NES_AEQE_TCP_STATE_MASK = (0xf<<24),
+ NES_AEQE_VALID = (1<<31),
+};
+
+#define NES_AEQE_IWARP_STATE_SHIFT 20
+#define NES_AEQE_TCP_STATE_SHIFT 24
+
+enum nes_aeqe_iwarp_state {
+ NES_AEQE_IWARP_STATE_NON_EXISTANT = 0,
+ NES_AEQE_IWARP_STATE_IDLE = 1,
+ NES_AEQE_IWARP_STATE_RTS = 2,
+ NES_AEQE_IWARP_STATE_CLOSING = 3,
+ NES_AEQE_IWARP_STATE_TERMINATE = 5,
+ NES_AEQE_IWARP_STATE_ERROR = 6
+};
+
+enum nes_aeqe_tcp_state {
+ NES_AEQE_TCP_STATE_NON_EXISTANT = 0,
+ NES_AEQE_TCP_STATE_CLOSED = 1,
+ NES_AEQE_TCP_STATE_LISTEN = 2,
+ NES_AEQE_TCP_STATE_SYN_SENT = 3,
+ NES_AEQE_TCP_STATE_SYN_RCVD = 4,
+ NES_AEQE_TCP_STATE_ESTABLISHED = 5,
+ NES_AEQE_TCP_STATE_CLOSE_WAIT = 6,
+ NES_AEQE_TCP_STATE_FIN_WAIT_1 = 7,
+ NES_AEQE_TCP_STATE_CLOSING = 8,
+ NES_AEQE_TCP_STATE_LAST_ACK = 9,
+ NES_AEQE_TCP_STATE_FIN_WAIT_2 = 10,
+ NES_AEQE_TCP_STATE_TIME_WAIT = 11
+};
+
+enum nes_aeqe_aeid {
+ NES_AEQE_AEID_AMP_UNALLOCATED_STAG = 0x0102,
+ NES_AEQE_AEID_AMP_INVALID_STAG = 0x0103,
+ NES_AEQE_AEID_AMP_BAD_QP = 0x0104,
+ NES_AEQE_AEID_AMP_BAD_PD = 0x0105,
+ NES_AEQE_AEID_AMP_BAD_STAG_KEY = 0x0106,
+ NES_AEQE_AEID_AMP_BAD_STAG_INDEX = 0x0107,
+ NES_AEQE_AEID_AMP_BOUNDS_VIOLATION = 0x0108,
+ NES_AEQE_AEID_AMP_RIGHTS_VIOLATION = 0x0109,
+ NES_AEQE_AEID_AMP_TO_WRAP = 0x010a,
+ NES_AEQE_AEID_AMP_FASTREG_SHARED = 0x010b,
+ NES_AEQE_AEID_AMP_FASTREG_VALID_STAG = 0x010c,
+ NES_AEQE_AEID_AMP_FASTREG_MW_STAG = 0x010d,
+ NES_AEQE_AEID_AMP_FASTREG_INVALID_RIGHTS = 0x010e,
+ NES_AEQE_AEID_AMP_FASTREG_PBL_TABLE_OVERFLOW = 0x010f,
+ NES_AEQE_AEID_AMP_FASTREG_INVALID_LENGTH = 0x0110,
+ NES_AEQE_AEID_AMP_INVALIDATE_SHARED = 0x0111,
+ NES_AEQE_AEID_AMP_INVALIDATE_NO_REMOTE_ACCESS_RIGHTS = 0x0112,
+ NES_AEQE_AEID_AMP_INVALIDATE_MR_WITH_BOUND_WINDOWS = 0x0113,
+ NES_AEQE_AEID_AMP_MWBIND_VALID_STAG = 0x0114,
+ NES_AEQE_AEID_AMP_MWBIND_OF_MR_STAG = 0x0115,
+ NES_AEQE_AEID_AMP_MWBIND_TO_ZERO_BASED_STAG = 0x0116,
+ NES_AEQE_AEID_AMP_MWBIND_TO_MW_STAG = 0x0117,
+ NES_AEQE_AEID_AMP_MWBIND_INVALID_RIGHTS = 0x0118,
+ NES_AEQE_AEID_AMP_MWBIND_INVALID_BOUNDS = 0x0119,
+ NES_AEQE_AEID_AMP_MWBIND_TO_INVALID_PARENT = 0x011a,
+ NES_AEQE_AEID_AMP_MWBIND_BIND_DISABLED = 0x011b,
+ NES_AEQE_AEID_BAD_CLOSE = 0x0201,
+ NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE = 0x0202,
+ NES_AEQE_AEID_CQ_OPERATION_ERROR = 0x0203,
+ NES_AEQE_AEID_PRIV_OPERATION_DENIED = 0x0204,
+ NES_AEQE_AEID_RDMA_READ_WHILE_ORD_ZERO = 0x0205,
+ NES_AEQE_AEID_STAG_ZERO_INVALID = 0x0206,
+ NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN = 0x0301,
+ NES_AEQE_AEID_DDP_INVALID_MSN_RANGE_IS_NOT_VALID = 0x0302,
+ NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER = 0x0303,
+ NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION = 0x0304,
+ NES_AEQE_AEID_DDP_UBE_INVALID_MO = 0x0305,
+ NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE = 0x0306,
+ NES_AEQE_AEID_DDP_UBE_INVALID_QN = 0x0307,
+ NES_AEQE_AEID_DDP_NO_L_BIT = 0x0308,
+ NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION = 0x0311,
+ NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE = 0x0312,
+ NES_AEQE_AEID_ROE_INVALID_RDMA_READ_REQUEST = 0x0313,
+ NES_AEQE_AEID_ROE_INVALID_RDMA_WRITE_OR_READ_RESP = 0x0314,
+ NES_AEQE_AEID_INVALID_ARP_ENTRY = 0x0401,
+ NES_AEQE_AEID_INVALID_TCP_OPTION_RCVD = 0x0402,
+ NES_AEQE_AEID_STALE_ARP_ENTRY = 0x0403,
+ NES_AEQE_AEID_LLP_CLOSE_COMPLETE = 0x0501,
+ NES_AEQE_AEID_LLP_CONNECTION_RESET = 0x0502,
+ NES_AEQE_AEID_LLP_FIN_RECEIVED = 0x0503,
+ NES_AEQE_AEID_LLP_RECEIVED_MARKER_AND_LENGTH_FIELDS_DONT_MATCH = 0x0504,
+ NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR = 0x0505,
+ NES_AEQE_AEID_LLP_SEGMENT_TOO_LARGE = 0x0506,
+ NES_AEQE_AEID_LLP_SEGMENT_TOO_SMALL = 0x0507,
+ NES_AEQE_AEID_LLP_SYN_RECEIVED = 0x0508,
+ NES_AEQE_AEID_LLP_TERMINATE_RECEIVED = 0x0509,
+ NES_AEQE_AEID_LLP_TOO_MANY_RETRIES = 0x050a,
+ NES_AEQE_AEID_LLP_TOO_MANY_KEEPALIVE_RETRIES = 0x050b,
+ NES_AEQE_AEID_RESET_SENT = 0x0601,
+ NES_AEQE_AEID_TERMINATE_SENT = 0x0602,
+ NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC = 0x0700
+};
+
+enum nes_iwarp_sq_opcodes {
+ NES_IWARP_SQ_WQE_WRPDU = (1<<15),
+ NES_IWARP_SQ_WQE_PSH = (1<<21),
+ NES_IWARP_SQ_WQE_STREAMING = (1<<23),
+ NES_IWARP_SQ_WQE_IMM_DATA = (1<<28),
+ NES_IWARP_SQ_WQE_READ_FENCE = (1<<29),
+ NES_IWARP_SQ_WQE_LOCAL_FENCE = (1<<30),
+ NES_IWARP_SQ_WQE_SIGNALED_COMPL = (1<<31),
+};
+
+enum nes_iwarp_sq_wqe_bits {
+ NES_IWARP_SQ_OP_RDMAW = 0,
+ NES_IWARP_SQ_OP_RDMAR = 1,
+ NES_IWARP_SQ_OP_SEND = 3,
+ NES_IWARP_SQ_OP_SENDINV = 4,
+ NES_IWARP_SQ_OP_SENDSE = 5,
+ NES_IWARP_SQ_OP_SENDSEINV = 6,
+ NES_IWARP_SQ_OP_BIND = 8,
+ NES_IWARP_SQ_OP_FAST_REG = 9,
+ NES_IWARP_SQ_OP_LOCINV = 10,
+ NES_IWARP_SQ_OP_RDMAR_LOCINV = 11,
+ NES_IWARP_SQ_OP_NOP = 12,
+};
+
+#define NES_EEPROM_READ_REQUEST (1<<16)
+#define NES_MAC_ADDR_VALID (1<<20)
+
+/*
+ * NES index registers init values.
+ */
+struct nes_init_values {
+ u32 index;
+ u32 data;
+ u8 wrt;
+};
+
+/*
+ * NES registers in BAR0.
+ */
+struct nes_pci_regs {
+ u32 int_status;
+ u32 int_mask;
+ u32 int_pending;
+ u32 intf_int_status;
+ u32 intf_int_mask;
+ u32 other_regs[59]; /* pad out to 256 bytes for now */
+};
+
+#define NES_CQP_SQ_SIZE 128
+#define NES_CCQ_SIZE 128
+#define NES_NIC_WQ_SIZE 512
+#define NES_NIC_CTX_SIZE ((NES_NIC_CTX_RQ_SIZE_512) | (NES_NIC_CTX_SQ_SIZE_512))
+#define NES_NIC_BACK_STORE 0x00038000
+
+struct nes_device;
+
+struct nes_hw_nic_qp_context {
+ __le32 context_words[6];
+};
+
+struct nes_hw_nic_sq_wqe {
+ __le32 wqe_words[16];
+};
+
+struct nes_hw_nic_rq_wqe {
+ __le32 wqe_words[16];
+};
+
+struct nes_hw_nic_cqe {
+ __le32 cqe_words[4];
+};
+
+struct nes_hw_cqp_qp_context {
+ __le32 context_words[4];
+};
+
+struct nes_hw_cqp_wqe {
+ __le32 wqe_words[16];
+};
+
+struct nes_hw_qp_wqe {
+ __le32 wqe_words[32];
+};
+
+struct nes_hw_cqe {
+ __le32 cqe_words[8];
+};
+
+struct nes_hw_ceqe {
+ __le32 ceqe_words[2];
+};
+
+struct nes_hw_aeqe {
+ __le32 aeqe_words[4];
+};
+
+struct nes_cqp_request {
+ union {
+ u64 cqp_callback_context;
+ void *cqp_callback_pointer;
+ };
+ wait_queue_head_t waitq;
+ struct nes_hw_cqp_wqe cqp_wqe;
+ struct list_head list;
+ atomic_t refcount;
+ void (*cqp_callback)(struct nes_device *nesdev, struct nes_cqp_request *cqp_request);
+ u16 major_code;
+ u16 minor_code;
+ u8 waiting;
+ u8 request_done;
+ u8 dynamic;
+ u8 callback;
+};
+
+struct nes_hw_cqp {
+ struct nes_hw_cqp_wqe *sq_vbase;
+ dma_addr_t sq_pbase;
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ u16 qp_id;
+ u16 sq_head;
+ u16 sq_tail;
+ u16 sq_size;
+};
+
+#define NES_FIRST_FRAG_SIZE 128
+struct nes_first_frag {
+ u8 buffer[NES_FIRST_FRAG_SIZE];
+};
+
+struct nes_hw_nic {
+ struct nes_first_frag *first_frag_vbase; /* virtual address of first frags */
+ struct nes_hw_nic_sq_wqe *sq_vbase; /* virtual address of sq */
+ struct nes_hw_nic_rq_wqe *rq_vbase; /* virtual address of rq */
+ struct sk_buff *tx_skb[NES_NIC_WQ_SIZE];
+ struct sk_buff *rx_skb[NES_NIC_WQ_SIZE];
+ dma_addr_t frag_paddr[NES_NIC_WQ_SIZE];
+ unsigned long first_frag_overflow[BITS_TO_LONGS(NES_NIC_WQ_SIZE)];
+ dma_addr_t sq_pbase; /* PCI memory for host rings */
+ dma_addr_t rq_pbase; /* PCI memory for host rings */
+
+ u16 qp_id;
+ u16 sq_head;
+ u16 sq_tail;
+ u16 sq_size;
+ u16 rq_head;
+ u16 rq_tail;
+ u16 rq_size;
+ u8 replenishing_rq;
+ u8 reserved;
+
+ spinlock_t sq_lock;
+ spinlock_t rq_lock;
+};
+
+struct nes_hw_nic_cq {
+ struct nes_hw_nic_cqe volatile *cq_vbase; /* PCI memory for host rings */
+ void (*ce_handler)(struct nes_device *nesdev, struct nes_hw_nic_cq *cq);
+ dma_addr_t cq_pbase; /* PCI memory for host rings */
+ int rx_cqes_completed;
+ int cqe_allocs_pending;
+ int rx_pkts_indicated;
+ u16 cq_head;
+ u16 cq_size;
+ u16 cq_number;
+ u8 cqes_pending;
+};
+
+struct nes_hw_qp {
+ struct nes_hw_qp_wqe *sq_vbase; /* PCI memory for host rings */
+ struct nes_hw_qp_wqe *rq_vbase; /* PCI memory for host rings */
+ void *q2_vbase; /* PCI memory for host rings */
+ dma_addr_t sq_pbase; /* PCI memory for host rings */
+ dma_addr_t rq_pbase; /* PCI memory for host rings */
+ dma_addr_t q2_pbase; /* PCI memory for host rings */
+ u32 qp_id;
+ u16 sq_head;
+ u16 sq_tail;
+ u16 sq_size;
+ u16 rq_head;
+ u16 rq_tail;
+ u16 rq_size;
+ u8 rq_encoded_size;
+ u8 sq_encoded_size;
+};
+
+struct nes_hw_cq {
+ struct nes_hw_cqe volatile *cq_vbase; /* PCI memory for host rings */
+ void (*ce_handler)(struct nes_device *nesdev, struct nes_hw_cq *cq);
+ dma_addr_t cq_pbase; /* PCI memory for host rings */
+ u16 cq_head;
+ u16 cq_size;
+ u16 cq_number;
+};
+
+struct nes_hw_ceq {
+ struct nes_hw_ceqe volatile *ceq_vbase; /* PCI memory for host rings */
+ dma_addr_t ceq_pbase; /* PCI memory for host rings */
+ u16 ceq_head;
+ u16 ceq_size;
+};
+
+struct nes_hw_aeq {
+ struct nes_hw_aeqe volatile *aeq_vbase; /* PCI memory for host rings */
+ dma_addr_t aeq_pbase; /* PCI memory for host rings */
+ u16 aeq_head;
+ u16 aeq_size;
+};
+
+struct nic_qp_map {
+ u8 qpid;
+ u8 nic_index;
+ u8 logical_port;
+ u8 is_hnic;
+};
+
+#define NES_CQP_ARP_AEQ_INDEX_MASK 0x000f0000
+#define NES_CQP_ARP_AEQ_INDEX_SHIFT 16
+
+#define NES_CQP_APBVT_ADD 0x00008000
+#define NES_CQP_APBVT_NIC_SHIFT 16
+
+#define NES_ARP_ADD 1
+#define NES_ARP_DELETE 2
+#define NES_ARP_RESOLVE 3
+
+#define NES_MAC_SW_IDLE 0
+#define NES_MAC_SW_INTERRUPT 1
+#define NES_MAC_SW_MH 2
+
+struct nes_arp_entry {
+ u32 ip_addr;
+ u8 mac_addr[ETH_ALEN];
+};
+
+#define NES_NIC_FAST_TIMER 96
+#define NES_NIC_FAST_TIMER_LOW 40
+#define NES_NIC_FAST_TIMER_HIGH 1000
+#define DEFAULT_NES_QL_HIGH 256
+#define DEFAULT_NES_QL_LOW 16
+#define DEFAULT_NES_QL_TARGET 64
+#define DEFAULT_JUMBO_NES_QL_LOW 12
+#define DEFAULT_JUMBO_NES_QL_TARGET 40
+#define DEFAULT_JUMBO_NES_QL_HIGH 128
+#define NES_NIC_CQ_DOWNWARD_TREND 8
+
+struct nes_hw_tune_timer {
+ //u16 cq_count;
+ u16 threshold_low;
+ u16 threshold_target;
+ u16 threshold_high;
+ u16 timer_in_use;
+ u16 timer_in_use_old;
+ u16 timer_in_use_min;
+ u16 timer_in_use_max;
+ u8 timer_direction_upward;
+ u8 timer_direction_downward;
+ u16 cq_count_old;
+ u8 cq_direction_downward;
+};
+
+#define NES_TIMER_INT_LIMIT 2
+#define NES_TIMER_INT_LIMIT_DYNAMIC 10
+#define NES_TIMER_ENABLE_LIMIT 4
+#define NES_MAX_LINK_INTERRUPTS 128
+#define NES_MAX_LINK_CHECK 200
+
+struct nes_adapter {
+ u64 fw_ver;
+ unsigned long *allocated_qps;
+ unsigned long *allocated_cqs;
+ unsigned long *allocated_mrs;
+ unsigned long *allocated_pds;
+ unsigned long *allocated_arps;
+ struct nes_qp **qp_table;
+ struct workqueue_struct *work_q;
+
+ struct list_head list;
+ struct list_head active_listeners;
+ /* list of the netdev's associated with each logical port */
+ struct list_head nesvnic_list[4];
+
+ struct timer_list mh_timer;
+ struct timer_list lc_timer;
+ struct work_struct work;
+ spinlock_t resource_lock;
+ spinlock_t phy_lock;
+ spinlock_t pbl_lock;
+ spinlock_t periodic_timer_lock;
+
+ struct nes_arp_entry arp_table[NES_MAX_ARP_TABLE_SIZE];
+
+ /* Adapter CEQ and AEQs */
+ struct nes_hw_ceq ceq[16];
+ struct nes_hw_aeq aeq[8];
+
+ struct nes_hw_tune_timer tune_timer;
+
+ unsigned long doorbell_start;
+
+ u32 hw_rev;
+ u32 vendor_id;
+ u32 vendor_part_id;
+ u32 device_cap_flags;
+ u32 tick_delta;
+ u32 timer_int_req;
+ u32 arp_table_size;
+ u32 next_arp_index;
+
+ u32 max_mr;
+ u32 max_256pbl;
+ u32 max_4kpbl;
+ u32 free_256pbl;
+ u32 free_4kpbl;
+ u32 max_mr_size;
+ u32 max_qp;
+ u32 next_qp;
+ u32 max_irrq;
+ u32 max_qp_wr;
+ u32 max_sge;
+ u32 max_cq;
+ u32 next_cq;
+ u32 max_cqe;
+ u32 max_pd;
+ u32 base_pd;
+ u32 next_pd;
+ u32 hte_index_mask;
+
+ /* EEPROM information */
+ u32 rx_pool_size;
+ u32 tx_pool_size;
+ u32 rx_threshold;
+ u32 tcp_timer_core_clk_divisor;
+ u32 iwarp_config;
+ u32 cm_config;
+ u32 sws_timer_config;
+ u32 tcp_config1;
+ u32 wqm_wat;
+ u32 core_clock;
+ u32 firmware_version;
+
+ u32 nic_rx_eth_route_err;
+
+ u32 et_rx_coalesce_usecs;
+ u32 et_rx_max_coalesced_frames;
+ u32 et_rx_coalesce_usecs_irq;
+ u32 et_rx_max_coalesced_frames_irq;
+ u32 et_pkt_rate_low;
+ u32 et_rx_coalesce_usecs_low;
+ u32 et_rx_max_coalesced_frames_low;
+ u32 et_pkt_rate_high;
+ u32 et_rx_coalesce_usecs_high;
+ u32 et_rx_max_coalesced_frames_high;
+ u32 et_rate_sample_interval;
+ u32 timer_int_limit;
+
+ /* Adapter base MAC address */
+ u32 mac_addr_low;
+ u16 mac_addr_high;
+
+ u16 firmware_eeprom_offset;
+ u16 software_eeprom_offset;
+
+ u16 max_irrq_wr;
+
+ /* pd config for each port */
+ u16 pd_config_size[4];
+ u16 pd_config_base[4];
+
+ u16 link_interrupt_count[4];
+
+ /* the phy index for each port */
+ u8 phy_index[4];
+ u8 mac_sw_state[4];
+ u8 mac_link_down[4];
+ u8 phy_type[4];
+
+ /* PCI information */
+ unsigned int devfn;
+ unsigned char bus_number;
+ unsigned char OneG_Mode;
+
+ unsigned char ref_count;
+ u8 netdev_count;
+ u8 netdev_max; /* from host nic address count in EEPROM */
+ u8 port_count;
+ u8 virtwq;
+ u8 et_use_adaptive_rx_coalesce;
+ u8 adapter_fcn_count;
+};
+
+struct nes_pbl {
+ u64 *pbl_vbase;
+ dma_addr_t pbl_pbase;
+ struct page *page;
+ unsigned long user_base;
+ u32 pbl_size;
+ struct list_head list;
+ /* TODO: need to add list for two level tables */
+};
+
+struct nes_listener {
+ struct work_struct work;
+ struct workqueue_struct *wq;
+ struct nes_vnic *nesvnic;
+ struct iw_cm_id *cm_id;
+ struct list_head list;
+ unsigned long socket;
+ u8 accept_failed;
+};
+
+struct nes_ib_device;
+
+struct nes_vnic {
+ struct nes_ib_device *nesibdev;
+ u64 sq_full;
+ u64 sq_locked;
+ u64 tso_requests;
+ u64 segmented_tso_requests;
+ u64 linearized_skbs;
+ u64 tx_sw_dropped;
+ u64 endnode_nstat_rx_discard;
+ u64 endnode_nstat_rx_octets;
+ u64 endnode_nstat_rx_frames;
+ u64 endnode_nstat_tx_octets;
+ u64 endnode_nstat_tx_frames;
+ u64 endnode_ipv4_tcp_retransmits;
+ /* void *mem; */
+ struct nes_device *nesdev;
+ struct net_device *netdev;
+ struct vlan_group *vlan_grp;
+ atomic_t rx_skbs_needed;
+ atomic_t rx_skb_timer_running;
+ int budget;
+ u32 msg_enable;
+ /* u32 tx_avail; */
+ __be32 local_ipaddr;
+ struct napi_struct napi;
+ spinlock_t tx_lock; /* could use netdev tx lock? */
+ struct timer_list rq_wqes_timer;
+ u32 nic_mem_size;
+ void *nic_vbase;
+ dma_addr_t nic_pbase;
+ struct nes_hw_nic nic;
+ struct nes_hw_nic_cq nic_cq;
+ u32 mcrq_qp_id;
+ struct nes_ucontext *mcrq_ucontext;
+ struct nes_cqp_request* (*get_cqp_request)(struct nes_device *nesdev);
+ void (*post_cqp_request)(struct nes_device*, struct nes_cqp_request *, int);
+ int (*mcrq_mcast_filter)( struct nes_vnic* nesvnic, __u8* dmi_addr );
+ struct net_device_stats netstats;
+ /* used to put the netdev on the adapters logical port list */
+ struct list_head list;
+ u16 max_frame_size;
+ u8 netdev_open;
+ u8 linkup;
+ u8 logical_port;
+ u8 netdev_index; /* might not be needed, indexes nesdev->netdev */
+ u8 perfect_filter_index;
+ u8 nic_index;
+ u8 qp_nic_index[4];
+ u8 next_qp_nic_index;
+ u8 of_device_registered;
+ u8 rdma_enabled;
+ u8 rx_checksum_disabled;
+};
+
+struct nes_ib_device {
+ struct ib_device ibdev;
+ struct nes_vnic *nesvnic;
+
+ /* Virtual RNIC Limits */
+ u32 max_mr;
+ u32 max_qp;
+ u32 max_cq;
+ u32 max_pd;
+ u32 num_mr;
+ u32 num_qp;
+ u32 num_cq;
+ u32 num_pd;
+};
+
+#define nes_vlan_rx vlan_hwaccel_receive_skb
+#define nes_netif_rx netif_receive_skb
+
+#endif /* __NES_HW_H */
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/if_vlan.h>
+#include <linux/ethtool.h>
+#include <net/tcp.h>
+
+#include <net/inet_common.h>
+#include <linux/inet.h>
+
+#include "nes.h"
+
+static struct nic_qp_map nic_qp_mapping_0[] = {
+ {16,0,0,1},{24,4,0,0},{28,8,0,0},{32,12,0,0},
+ {20,2,2,1},{26,6,2,0},{30,10,2,0},{34,14,2,0},
+ {18,1,1,1},{25,5,1,0},{29,9,1,0},{33,13,1,0},
+ {22,3,3,1},{27,7,3,0},{31,11,3,0},{35,15,3,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_1[] = {
+ {18,1,1,1},{25,5,1,0},{29,9,1,0},{33,13,1,0},
+ {22,3,3,1},{27,7,3,0},{31,11,3,0},{35,15,3,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_2[] = {
+ {20,2,2,1},{26,6,2,0},{30,10,2,0},{34,14,2,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_3[] = {
+ {22,3,3,1},{27,7,3,0},{31,11,3,0},{35,15,3,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_4[] = {
+ {28,8,0,0},{32,12,0,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_5[] = {
+ {29,9,1,0},{33,13,1,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_6[] = {
+ {30,10,2,0},{34,14,2,0}
+};
+
+static struct nic_qp_map nic_qp_mapping_7[] = {
+ {31,11,3,0},{35,15,3,0}
+};
+
+static struct nic_qp_map *nic_qp_mapping_per_function[] = {
+ nic_qp_mapping_0, nic_qp_mapping_1, nic_qp_mapping_2, nic_qp_mapping_3,
+ nic_qp_mapping_4, nic_qp_mapping_5, nic_qp_mapping_6, nic_qp_mapping_7
+};
+
+static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
+ | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
+static int debug = -1;
+
+
+static int nes_netdev_open(struct net_device *);
+static int nes_netdev_stop(struct net_device *);
+static int nes_netdev_start_xmit(struct sk_buff *, struct net_device *);
+static struct net_device_stats *nes_netdev_get_stats(struct net_device *);
+static void nes_netdev_tx_timeout(struct net_device *);
+static int nes_netdev_set_mac_address(struct net_device *, void *);
+static int nes_netdev_change_mtu(struct net_device *, int);
+
+/**
+ * nes_netdev_poll
+ */
+static int nes_netdev_poll(struct napi_struct *napi, int budget)
+{
+ struct nes_vnic *nesvnic = container_of(napi, struct nes_vnic, napi);
+ struct net_device *netdev = nesvnic->netdev;
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_hw_nic_cq *nescq = &nesvnic->nic_cq;
+
+ nesvnic->budget = budget;
+ nescq->cqes_pending = 0;
+ nescq->rx_cqes_completed = 0;
+ nescq->cqe_allocs_pending = 0;
+ nescq->rx_pkts_indicated = 0;
+
+ nes_nic_ce_handler(nesdev, nescq);
+
+ if (nescq->cqes_pending == 0) {
+ netif_rx_complete(netdev, napi);
+ /* clear out completed cqes and arm */
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ nescq->cq_number | (nescq->cqe_allocs_pending << 16));
+ nes_read32(nesdev->regs+NES_CQE_ALLOC);
+ } else {
+ /* clear out completed cqes but don't arm */
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
+ nescq->cq_number | (nescq->cqe_allocs_pending << 16));
+ nes_debug(NES_DBG_NETDEV, "%s: exiting with work pending\n",
+ nesvnic->netdev->name);
+ }
+ return nescq->rx_pkts_indicated;
+}
+
+
+/**
+ * nes_netdev_open - Activate the network interface; ifconfig
+ * ethx up.
+ */
+static int nes_netdev_open(struct net_device *netdev)
+{
+ u32 macaddr_low;
+ u16 macaddr_high;
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ int ret;
+ int i;
+ struct nes_vnic *first_nesvnic;
+ u32 nic_active_bit;
+ u32 nic_active;
+
+ assert(nesdev != NULL);
+
+ first_nesvnic = list_entry(nesdev->nesadapter->nesvnic_list[nesdev->mac_index].next,
+ struct nes_vnic, list);
+
+ if (netif_msg_ifup(nesvnic))
+ printk(KERN_INFO PFX "%s: enabling interface\n", netdev->name);
+
+ ret = nes_init_nic_qp(nesdev, netdev);
+ if (ret) {
+ return ret;
+ }
+
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+
+ if ((!nesvnic->of_device_registered) && (nesvnic->rdma_enabled)) {
+ nesvnic->nesibdev = nes_init_ofa_device(netdev);
+ if (nesvnic->nesibdev == NULL) {
+ printk(KERN_ERR PFX "%s: nesvnic->nesibdev alloc failed", netdev->name);
+ } else {
+ nesvnic->nesibdev->nesvnic = nesvnic;
+ ret = nes_register_ofa_device(nesvnic->nesibdev);
+ if (ret) {
+ printk(KERN_ERR PFX "%s: Unable to register RDMA device, ret = %d\n",
+ netdev->name, ret);
+ }
+ }
+ }
+ /* Set packet filters */
+ nic_active_bit = 1 << nesvnic->nic_index;
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_ACTIVE, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ENABLE);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ENABLE, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_BROADCAST_ON);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_BROADCAST_ON, nic_active);
+
+ macaddr_high = ((u16)netdev->dev_addr[0]) << 8;
+ macaddr_high += (u16)netdev->dev_addr[1];
+ macaddr_low = ((u32)netdev->dev_addr[2]) << 24;
+ macaddr_low += ((u32)netdev->dev_addr[3]) << 16;
+ macaddr_low += ((u32)netdev->dev_addr[4]) << 8;
+ macaddr_low += (u32)netdev->dev_addr[5];
+
+ /* Program the various MAC regs */
+ for (i = 0; i < NES_MAX_PORT_COUNT; i++) {
+ if (nesvnic->qp_nic_index[i] == 0xf) {
+ break;
+ }
+ nes_debug(NES_DBG_NETDEV, "i=%d, perfect filter table index= %d, PERF FILTER LOW"
+ " (Addr:%08X) = %08X, HIGH = %08X.\n",
+ i, nesvnic->qp_nic_index[i],
+ NES_IDX_PERFECT_FILTER_LOW+((nesvnic->perfect_filter_index + i) * 8),
+ macaddr_low,
+ (u32)macaddr_high | NES_MAC_ADDR_VALID |
+ ((((u32)nesvnic->nic_index) << 16)));
+ nes_write_indexed(nesdev,
+ NES_IDX_PERFECT_FILTER_LOW + (nesvnic->qp_nic_index[i] * 8),
+ macaddr_low);
+ nes_write_indexed(nesdev,
+ NES_IDX_PERFECT_FILTER_HIGH + (nesvnic->qp_nic_index[i] * 8),
+ (u32)macaddr_high | NES_MAC_ADDR_VALID |
+ ((((u32)nesvnic->nic_index) << 16)));
+ }
+
+
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
+ nesvnic->nic_cq.cq_number);
+ nes_read32(nesdev->regs+NES_CQE_ALLOC);
+
+ if (first_nesvnic->linkup) {
+ /* Enable network packets */
+ nesvnic->linkup = 1;
+ netif_start_queue(netdev);
+ netif_carrier_on(netdev);
+ }
+ napi_enable(&nesvnic->napi);
+ nesvnic->netdev_open = 1;
+
+ return 0;
+}
+
+
+/**
+ * nes_netdev_stop
+ */
+static int nes_netdev_stop(struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ u32 nic_active_mask;
+ u32 nic_active;
+
+ nes_debug(NES_DBG_SHUTDOWN, "nesvnic=%p, nesdev=%p, netdev=%p %s\n",
+ nesvnic, nesdev, netdev, netdev->name);
+ if (nesvnic->netdev_open == 0)
+ return 0;
+
+ if (netif_msg_ifdown(nesvnic))
+ printk(KERN_INFO PFX "%s: disabling interface\n", netdev->name);
+
+ /* Disable network packets */
+ napi_disable(&nesvnic->napi);
+ netif_stop_queue(netdev);
+ if ((nesdev->netdev[0] == netdev) & (nesvnic->logical_port == nesdev->mac_index)) {
+ nes_write_indexed(nesdev,
+ NES_IDX_MAC_INT_MASK+(0x200*nesdev->mac_index), 0xffffffff);
+ }
+
+ nic_active_mask = ~((u32)(1 << nesvnic->nic_index));
+ nes_write_indexed(nesdev, NES_IDX_PERFECT_FILTER_HIGH+
+ (nesvnic->perfect_filter_index*8), 0);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_ACTIVE);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev, NES_IDX_NIC_ACTIVE, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ENABLE);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ENABLE, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_BROADCAST_ON);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev, NES_IDX_NIC_BROADCAST_ON, nic_active);
+
+
+ if (nesvnic->of_device_registered) {
+ nes_destroy_ofa_device(nesvnic->nesibdev);
+ nesvnic->nesibdev = NULL;
+ nesvnic->of_device_registered = 0;
+ }
+ nes_destroy_nic_qp(nesvnic);
+
+ nesvnic->netdev_open = 0;
+
+ return 0;
+}
+
+
+/**
+ * nes_nic_send
+ */
+static int nes_nic_send(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_hw_nic *nesnic = &nesvnic->nic;
+ struct nes_hw_nic_sq_wqe *nic_sqe;
+ struct tcphdr *tcph;
+ __le16 *wqe_fragment_length;
+ u32 wqe_misc;
+ u16 wqe_fragment_index = 1; /* first fragment (0) is used by copy buffer */
+ u16 skb_fragment_index;
+ dma_addr_t bus_address;
+
+ nic_sqe = &nesnic->sq_vbase[nesnic->sq_head];
+ wqe_fragment_length = (__le16 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX];
+
+ /* setup the VLAN tag if present */
+ if (vlan_tx_tag_present(skb)) {
+ nes_debug(NES_DBG_NIC_TX, "%s: VLAN packet to send... VLAN = %08X\n",
+ netdev->name, vlan_tx_tag_get(skb));
+ wqe_misc = NES_NIC_SQ_WQE_TAGVALUE_ENABLE;
+ wqe_fragment_length[0] = (__force __le16) vlan_tx_tag_get(skb);
+ } else
+ wqe_misc = 0;
+
+ /* bump past the vlan tag */
+ wqe_fragment_length++;
+ /* wqe_fragment_address = (u64 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG0_LOW_IDX]; */
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ tcph = tcp_hdr(skb);
+ if (1) {
+ if (skb_is_gso(skb)) {
+ /* nes_debug(NES_DBG_NIC_TX, "%s: TSO request... seg size = %u\n",
+ netdev->name, skb_is_gso(skb)); */
+ wqe_misc |= NES_NIC_SQ_WQE_LSO_ENABLE |
+ NES_NIC_SQ_WQE_COMPLETION | (u16)skb_is_gso(skb);
+ set_wqe_32bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_LSO_INFO_IDX,
+ ((u32)tcph->doff) |
+ (((u32)(((unsigned char *)tcph) - skb->data)) << 4));
+ } else {
+ wqe_misc |= NES_NIC_SQ_WQE_COMPLETION;
+ }
+ }
+ } else { /* CHECKSUM_HW */
+ wqe_misc |= NES_NIC_SQ_WQE_DISABLE_CHKSUM | NES_NIC_SQ_WQE_COMPLETION;
+ }
+
+ set_wqe_32bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_TOTAL_LENGTH_IDX,
+ skb->len);
+ memcpy(&nesnic->first_frag_vbase[nesnic->sq_head].buffer,
+ skb->data, min(((unsigned int)NES_FIRST_FRAG_SIZE), skb_headlen(skb)));
+ wqe_fragment_length[0] = cpu_to_le16(min(((unsigned int)NES_FIRST_FRAG_SIZE),
+ skb_headlen(skb)));
+ wqe_fragment_length[1] = 0;
+ if (skb_headlen(skb) > NES_FIRST_FRAG_SIZE) {
+ if ((skb_shinfo(skb)->nr_frags + 1) > 4) {
+ nes_debug(NES_DBG_NIC_TX, "%s: Packet with %u fragments not sent, skb_headlen=%u\n",
+ netdev->name, skb_shinfo(skb)->nr_frags + 2, skb_headlen(skb));
+ kfree_skb(skb);
+ nesvnic->tx_sw_dropped++;
+ return NETDEV_TX_LOCKED;
+ }
+ set_bit(nesnic->sq_head, nesnic->first_frag_overflow);
+ bus_address = pci_map_single(nesdev->pcidev, skb->data + NES_FIRST_FRAG_SIZE,
+ skb_headlen(skb) - NES_FIRST_FRAG_SIZE, PCI_DMA_TODEVICE);
+ wqe_fragment_length[wqe_fragment_index++] =
+ cpu_to_le16(skb_headlen(skb) - NES_FIRST_FRAG_SIZE);
+ wqe_fragment_length[wqe_fragment_index] = 0;
+ set_wqe_64bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_LOW_IDX,
+ ((u64)(bus_address)));
+ nesnic->tx_skb[nesnic->sq_head] = skb;
+ }
+
+ if (skb_headlen(skb) == skb->len) {
+ if (skb_headlen(skb) <= NES_FIRST_FRAG_SIZE) {
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_2_1_IDX] = 0;
+ nesnic->tx_skb[nesnic->sq_head] = NULL;
+ dev_kfree_skb(skb);
+ }
+ } else {
+ /* Deal with Fragments */
+ nesnic->tx_skb[nesnic->sq_head] = skb;
+ for (skb_fragment_index = 0; skb_fragment_index < skb_shinfo(skb)->nr_frags;
+ skb_fragment_index++) {
+ bus_address = pci_map_page( nesdev->pcidev,
+ skb_shinfo(skb)->frags[skb_fragment_index].page,
+ skb_shinfo(skb)->frags[skb_fragment_index].page_offset,
+ skb_shinfo(skb)->frags[skb_fragment_index].size,
+ PCI_DMA_TODEVICE);
+ wqe_fragment_length[wqe_fragment_index] =
+ cpu_to_le16(skb_shinfo(skb)->frags[skb_fragment_index].size);
+ set_wqe_64bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX+(2*wqe_fragment_index),
+ bus_address);
+ wqe_fragment_index++;
+ if (wqe_fragment_index < 5)
+ wqe_fragment_length[wqe_fragment_index] = 0;
+ }
+ }
+
+ set_wqe_32bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_MISC_IDX, wqe_misc);
+ nesnic->sq_head++;
+ nesnic->sq_head &= nesnic->sq_size - 1;
+
+ return NETDEV_TX_OK;
+}
+
+
+/**
+ * nes_netdev_start_xmit
+ */
+static int nes_netdev_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_hw_nic *nesnic = &nesvnic->nic;
+ struct nes_hw_nic_sq_wqe *nic_sqe;
+ struct tcphdr *tcph;
+ /* struct udphdr *udph; */
+#define NES_MAX_TSO_FRAGS 18
+ /* 64K segment plus overflow on each side */
+ dma_addr_t tso_bus_address[NES_MAX_TSO_FRAGS];
+ dma_addr_t bus_address;
+ u32 tso_frag_index;
+ u32 tso_frag_count;
+ u32 tso_wqe_length;
+ u32 curr_tcp_seq;
+ u32 wqe_count=1;
+ u32 send_rc;
+ struct iphdr *iph;
+ unsigned long flags;
+ __le16 *wqe_fragment_length;
+ u32 nr_frags;
+ u32 original_first_length;
+// u64 *wqe_fragment_address;
+ /* first fragment (0) is used by copy buffer */
+ u16 wqe_fragment_index=1;
+ u16 hoffset;
+ u16 nhoffset;
+ u16 wqes_needed;
+ u16 wqes_available;
+ u32 old_head;
+ u32 wqe_misc;
+
+ /* nes_debug(NES_DBG_NIC_TX, "%s Request to tx NIC packet length %u, headlen %u,"
+ " (%u frags), tso_size=%u\n",
+ netdev->name, skb->len, skb_headlen(skb),
+ skb_shinfo(skb)->nr_frags, skb_is_gso(skb));
+ */
+
+ if (!netif_carrier_ok(netdev))
+ return NETDEV_TX_OK;
+
+ if (netif_queue_stopped(netdev))
+ return NETDEV_TX_BUSY;
+
+ local_irq_save(flags);
+ if (!spin_trylock(&nesnic->sq_lock)) {
+ local_irq_restore(flags);
+ nesvnic->sq_locked++;
+ return NETDEV_TX_LOCKED;
+ }
+
+ /* Check if SQ is full */
+ if ((((nesnic->sq_tail+(nesnic->sq_size*2))-nesnic->sq_head) & (nesnic->sq_size - 1)) == 1) {
+ if (!netif_queue_stopped(netdev)) {
+ netif_stop_queue(netdev);
+ barrier();
+ if ((((((volatile u16)nesnic->sq_tail)+(nesnic->sq_size*2))-nesnic->sq_head) & (nesnic->sq_size - 1)) != 1) {
+ netif_start_queue(netdev);
+ goto sq_no_longer_full;
+ }
+ }
+ nesvnic->sq_full++;
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+sq_no_longer_full:
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ if (skb_headlen(skb) > NES_FIRST_FRAG_SIZE) {
+ nr_frags++;
+ }
+ /* Check if too many fragments */
+ if (unlikely((nr_frags > 4))) {
+ if (skb_is_gso(skb)) {
+ nesvnic->segmented_tso_requests++;
+ nesvnic->tso_requests++;
+ old_head = nesnic->sq_head;
+ /* Basically 4 fragments available per WQE with extended fragments */
+ wqes_needed = nr_frags >> 2;
+ wqes_needed += (nr_frags&3)?1:0;
+ wqes_available = (((nesnic->sq_tail+nesnic->sq_size)-nesnic->sq_head) - 1) &
+ (nesnic->sq_size - 1);
+
+ if (unlikely(wqes_needed > wqes_available)) {
+ if (!netif_queue_stopped(netdev)) {
+ netif_stop_queue(netdev);
+ barrier();
+ wqes_available = (((((volatile u16)nesnic->sq_tail)+nesnic->sq_size)-nesnic->sq_head) - 1) &
+ (nesnic->sq_size - 1);
+ if (wqes_needed <= wqes_available) {
+ netif_start_queue(netdev);
+ goto tso_sq_no_longer_full;
+ }
+ }
+ nesvnic->sq_full++;
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+ nes_debug(NES_DBG_NIC_TX, "%s: HNIC SQ full- TSO request has too many frags!\n",
+ netdev->name);
+ return NETDEV_TX_BUSY;
+ }
+tso_sq_no_longer_full:
+ /* Map all the buffers */
+ for (tso_frag_count=0; tso_frag_count < skb_shinfo(skb)->nr_frags;
+ tso_frag_count++) {
+ tso_bus_address[tso_frag_count] = pci_map_page( nesdev->pcidev,
+ skb_shinfo(skb)->frags[tso_frag_count].page,
+ skb_shinfo(skb)->frags[tso_frag_count].page_offset,
+ skb_shinfo(skb)->frags[tso_frag_count].size,
+ PCI_DMA_TODEVICE);
+ }
+
+ tso_frag_index = 0;
+ curr_tcp_seq = ntohl(tcp_hdr(skb)->seq);
+ hoffset = skb_transport_header(skb) - skb->data;
+ nhoffset = skb_network_header(skb) - skb->data;
+ original_first_length = hoffset + ((((struct tcphdr *)skb_transport_header(skb))->doff)<<2);
+
+ for (wqe_count=0; wqe_count<((u32)wqes_needed); wqe_count++) {
+ tso_wqe_length = 0;
+ nic_sqe = &nesnic->sq_vbase[nesnic->sq_head];
+ wqe_fragment_length =
+ (__le16 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX];
+ /* setup the VLAN tag if present */
+ if (vlan_tx_tag_present(skb)) {
+ nes_debug(NES_DBG_NIC_TX, "%s: VLAN packet to send... VLAN = %08X\n",
+ netdev->name, vlan_tx_tag_get(skb) );
+ wqe_misc = NES_NIC_SQ_WQE_TAGVALUE_ENABLE;
+ wqe_fragment_length[0] = (__force __le16) vlan_tx_tag_get(skb);
+ } else
+ wqe_misc = 0;
+
+ /* bump past the vlan tag */
+ wqe_fragment_length++;
+
+ /* Assumes header totally fits in allocated buffer and is in first fragment */
+ if (original_first_length > NES_FIRST_FRAG_SIZE) {
+ nes_debug(NES_DBG_NIC_TX, "ERROR: SKB header too big, headlen=%u, FIRST_FRAG_SIZE=%u\n",
+ original_first_length, NES_FIRST_FRAG_SIZE);
+ nes_debug(NES_DBG_NIC_TX, "%s Request to tx NIC packet length %u, headlen %u,"
+ " (%u frags), tso_size=%u\n",
+ netdev->name,
+ skb->len, skb_headlen(skb),
+ skb_shinfo(skb)->nr_frags, skb_is_gso(skb));
+ }
+ memcpy(&nesnic->first_frag_vbase[nesnic->sq_head].buffer,
+ skb->data, min(((unsigned int)NES_FIRST_FRAG_SIZE),
+ original_first_length));
+ iph = (struct iphdr *)
+ (&nesnic->first_frag_vbase[nesnic->sq_head].buffer[nhoffset]);
+ tcph = (struct tcphdr *)
+ (&nesnic->first_frag_vbase[nesnic->sq_head].buffer[hoffset]);
+ if ((wqe_count+1)!=(u32)wqes_needed) {
+ tcph->fin = 0;
+ tcph->psh = 0;
+ tcph->rst = 0;
+ tcph->urg = 0;
+ }
+ if (wqe_count) {
+ tcph->syn = 0;
+ }
+ tcph->seq = htonl(curr_tcp_seq);
+ wqe_fragment_length[0] = cpu_to_le16(min(((unsigned int)NES_FIRST_FRAG_SIZE),
+ original_first_length));
+
+ wqe_fragment_index = 1;
+ if ((wqe_count==0) && (skb_headlen(skb) > original_first_length)) {
+ set_bit(nesnic->sq_head, nesnic->first_frag_overflow);
+ bus_address = pci_map_single(nesdev->pcidev, skb->data + original_first_length,
+ skb_headlen(skb) - original_first_length, PCI_DMA_TODEVICE);
+ wqe_fragment_length[wqe_fragment_index++] =
+ cpu_to_le16(skb_headlen(skb) - original_first_length);
+ wqe_fragment_length[wqe_fragment_index] = 0;
+ set_wqe_64bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_LOW_IDX,
+ bus_address);
+ }
+ while (wqe_fragment_index < 5) {
+ wqe_fragment_length[wqe_fragment_index] =
+ cpu_to_le16(skb_shinfo(skb)->frags[tso_frag_index].size);
+ set_wqe_64bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX+(2*wqe_fragment_index),
+ (u64)tso_bus_address[tso_frag_index]);
+ wqe_fragment_index++;
+ tso_wqe_length += skb_shinfo(skb)->frags[tso_frag_index++].size;
+ if (wqe_fragment_index < 5)
+ wqe_fragment_length[wqe_fragment_index] = 0;
+ if (tso_frag_index == tso_frag_count)
+ break;
+ }
+ if ((wqe_count+1) == (u32)wqes_needed) {
+ nesnic->tx_skb[nesnic->sq_head] = skb;
+ } else {
+ nesnic->tx_skb[nesnic->sq_head] = NULL;
+ }
+ wqe_misc |= NES_NIC_SQ_WQE_COMPLETION | (u16)skb_is_gso(skb);
+ if ((tso_wqe_length + original_first_length) > skb_is_gso(skb)) {
+ wqe_misc |= NES_NIC_SQ_WQE_LSO_ENABLE;
+ } else {
+ iph->tot_len = htons(tso_wqe_length + original_first_length - nhoffset);
+ }
+
+ set_wqe_32bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_MISC_IDX,
+ wqe_misc);
+ set_wqe_32bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_LSO_INFO_IDX,
+ ((u32)tcph->doff) | (((u32)hoffset) << 4));
+
+ set_wqe_32bit_value(nic_sqe->wqe_words, NES_NIC_SQ_WQE_TOTAL_LENGTH_IDX,
+ tso_wqe_length + original_first_length);
+ curr_tcp_seq += tso_wqe_length;
+ nesnic->sq_head++;
+ nesnic->sq_head &= nesnic->sq_size-1;
+ }
+ } else {
+ nesvnic->linearized_skbs++;
+ hoffset = skb_transport_header(skb) - skb->data;
+ nhoffset = skb_network_header(skb) - skb->data;
+ skb_linearize(skb);
+ skb_set_transport_header(skb, hoffset);
+ skb_set_network_header(skb, nhoffset);
+ send_rc = nes_nic_send(skb, netdev);
+ if (send_rc != NETDEV_TX_OK) {
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+ return NETDEV_TX_OK;
+ }
+ }
+ } else {
+ send_rc = nes_nic_send(skb, netdev);
+ if (send_rc != NETDEV_TX_OK) {
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+ return NETDEV_TX_OK;
+ }
+ }
+
+ barrier();
+
+ if (wqe_count)
+ nes_write32(nesdev->regs+NES_WQE_ALLOC,
+ (wqe_count << 24) | (1 << 23) | nesvnic->nic.qp_id);
+
+ netdev->trans_start = jiffies;
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+
+/**
+ * nes_netdev_get_stats
+ */
+static struct net_device_stats *nes_netdev_get_stats(struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ u64 u64temp;
+ u32 u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_DISCARD + (nesvnic->nic_index*0x200));
+ nesvnic->netstats.rx_dropped += u32temp;
+ nesvnic->endnode_nstat_rx_discard += u32temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_OCTETS_LO + (nesvnic->nic_index*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_OCTETS_HI + (nesvnic->nic_index*0x200))) << 32;
+
+ nesvnic->endnode_nstat_rx_octets += u64temp;
+ nesvnic->netstats.rx_bytes += u64temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_FRAMES_LO + (nesvnic->nic_index*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_FRAMES_HI + (nesvnic->nic_index*0x200))) << 32;
+
+ nesvnic->endnode_nstat_rx_frames += u64temp;
+ nesvnic->netstats.rx_packets += u64temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_OCTETS_LO + (nesvnic->nic_index*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_OCTETS_HI + (nesvnic->nic_index*0x200))) << 32;
+
+ nesvnic->endnode_nstat_tx_octets += u64temp;
+ nesvnic->netstats.tx_bytes += u64temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_FRAMES_LO + (nesvnic->nic_index*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_FRAMES_HI + (nesvnic->nic_index*0x200))) << 32;
+
+ nesvnic->endnode_nstat_tx_frames += u64temp;
+ nesvnic->netstats.tx_packets += u64temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_SHORT_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->netstats.rx_dropped += u32temp;
+ nesvnic->nesdev->mac_rx_errors += u32temp;
+ nesvnic->nesdev->mac_rx_short_frames += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_OVERSIZED_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->netstats.rx_dropped += u32temp;
+ nesvnic->nesdev->mac_rx_errors += u32temp;
+ nesvnic->nesdev->mac_rx_oversized_frames += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_JABBER_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->netstats.rx_dropped += u32temp;
+ nesvnic->nesdev->mac_rx_errors += u32temp;
+ nesvnic->nesdev->mac_rx_jabber_frames += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_SYMBOL_ERR_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->netstats.rx_dropped += u32temp;
+ nesvnic->nesdev->mac_rx_errors += u32temp;
+ nesvnic->nesdev->mac_rx_symbol_err_frames += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_LENGTH_ERR_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->netstats.rx_length_errors += u32temp;
+ nesvnic->nesdev->mac_rx_errors += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_CRC_ERR_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->nesdev->mac_rx_errors += u32temp;
+ nesvnic->nesdev->mac_rx_crc_errors += u32temp;
+ nesvnic->netstats.rx_crc_errors += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_TX_ERRORS + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->nesdev->mac_tx_errors += u32temp;
+ nesvnic->netstats.tx_errors += u32temp;
+
+ return &nesvnic->netstats;
+}
+
+
+/**
+ * nes_netdev_tx_timeout
+ */
+static void nes_netdev_tx_timeout(struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+
+ if (netif_msg_timer(nesvnic))
+ nes_debug(NES_DBG_NIC_TX, "%s: tx timeout\n", netdev->name);
+}
+
+
+/**
+ * nes_netdev_set_mac_address
+ */
+static int nes_netdev_set_mac_address(struct net_device *netdev, void *p)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct sockaddr *mac_addr = p;
+ int i;
+ u32 macaddr_low;
+ u16 macaddr_high;
+
+ if (!is_valid_ether_addr(mac_addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(netdev->dev_addr, mac_addr->sa_data, netdev->addr_len);
+ printk(PFX "%s: Address length = %d, Address = %02X%02X%02X%02X%02X%02X..\n",
+ __FUNCTION__, netdev->addr_len,
+ mac_addr->sa_data[0], mac_addr->sa_data[1],
+ mac_addr->sa_data[2], mac_addr->sa_data[3],
+ mac_addr->sa_data[4], mac_addr->sa_data[5]);
+ macaddr_high = ((u16)netdev->dev_addr[0]) << 8;
+ macaddr_high += (u16)netdev->dev_addr[1];
+ macaddr_low = ((u32)netdev->dev_addr[2]) << 24;
+ macaddr_low += ((u32)netdev->dev_addr[3]) << 16;
+ macaddr_low += ((u32)netdev->dev_addr[4]) << 8;
+ macaddr_low += (u32)netdev->dev_addr[5];
+
+ for (i = 0; i < NES_MAX_PORT_COUNT; i++) {
+ if (nesvnic->qp_nic_index[i] == 0xf) {
+ break;
+ }
+ nes_write_indexed(nesdev,
+ NES_IDX_PERFECT_FILTER_LOW + (nesvnic->qp_nic_index[i] * 8),
+ macaddr_low);
+ nes_write_indexed(nesdev,
+ NES_IDX_PERFECT_FILTER_HIGH + (nesvnic->qp_nic_index[i] * 8),
+ (u32)macaddr_high | NES_MAC_ADDR_VALID |
+ ((((u32)nesvnic->nic_index) << 16)));
+ }
+ return 0;
+}
+
+
+/**
+ * nes_netdev_set_multicast_list
+ */
+void nes_netdev_set_multicast_list(struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct dev_mc_list *multicast_addr;
+ u32 nic_active_bit;
+ u32 nic_active;
+ u32 perfect_filter_register_address;
+ u32 macaddr_low;
+ u16 macaddr_high;
+ u8 mc_all_on = 0;
+ u8 mc_index;
+ int mc_nic_index = -1;
+
+ nic_active_bit = 1 << nesvnic->nic_index;
+
+ if (netdev->flags & IFF_PROMISC) {
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL, nic_active);
+ mc_all_on = 1;
+ } else if ((netdev->flags & IFF_ALLMULTI) || (netdev->mc_count > NES_MULTICAST_PF_MAX) ||
+ (nesvnic->nic_index > 3)) {
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL);
+ nic_active &= ~nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL, nic_active);
+ mc_all_on = 1;
+ } else {
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL);
+ nic_active &= ~nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_MULTICAST_ALL, nic_active);
+ nic_active = nes_read_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL);
+ nic_active &= ~nic_active_bit;
+ nes_write_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL, nic_active);
+ }
+
+ nes_debug(NES_DBG_NIC_RX, "Number of MC entries = %d, Promiscous = %d, All Multicast = %d.\n",
+ netdev->mc_count, (netdev->flags & IFF_PROMISC)?1:0,
+ (netdev->flags & IFF_ALLMULTI)?1:0);
+ if (!mc_all_on) {
+ multicast_addr = netdev->mc_list;
+ perfect_filter_register_address = NES_IDX_PERFECT_FILTER_LOW + 0x80;
+ perfect_filter_register_address += nesvnic->nic_index*0x40;
+ for (mc_index=0; mc_index < NES_MULTICAST_PF_MAX; mc_index++) {
+ while (multicast_addr && nesvnic->mcrq_mcast_filter && ((mc_nic_index = nesvnic->mcrq_mcast_filter(nesvnic, multicast_addr->dmi_addr)) == 0))
+ multicast_addr = multicast_addr->next;
+
+ if (mc_nic_index < 0)
+ mc_nic_index = nesvnic->nic_index;
+ if (multicast_addr) {
+ nes_debug(NES_DBG_NIC_RX, "Assigning MC Address = %02X%02X%02X%02X%02X%02X to register 0x%04X nic_idx=%d\n",
+ multicast_addr->dmi_addr[0], multicast_addr->dmi_addr[1],
+ multicast_addr->dmi_addr[2], multicast_addr->dmi_addr[3],
+ multicast_addr->dmi_addr[4], multicast_addr->dmi_addr[5],
+ perfect_filter_register_address+(mc_index * 8), mc_nic_index);
+ macaddr_high = ((u16)multicast_addr->dmi_addr[0]) << 8;
+ macaddr_high += (u16)multicast_addr->dmi_addr[1];
+ macaddr_low = ((u32)multicast_addr->dmi_addr[2]) << 24;
+ macaddr_low += ((u32)multicast_addr->dmi_addr[3]) << 16;
+ macaddr_low += ((u32)multicast_addr->dmi_addr[4]) << 8;
+ macaddr_low += (u32)multicast_addr->dmi_addr[5];
+ nes_write_indexed(nesdev,
+ perfect_filter_register_address+(mc_index * 8),
+ macaddr_low);
+ nes_write_indexed(nesdev,
+ perfect_filter_register_address+4+(mc_index * 8),
+ (u32)macaddr_high | NES_MAC_ADDR_VALID |
+ ((((u32)(1<<mc_nic_index)) << 16)));
+ multicast_addr = multicast_addr->next;
+ } else {
+ nes_debug(NES_DBG_NIC_RX, "Clearing MC Address at register 0x%04X\n",
+ perfect_filter_register_address+(mc_index * 8));
+ nes_write_indexed(nesdev,
+ perfect_filter_register_address+4+(mc_index * 8),
+ 0);
+ }
+ }
+ }
+}
+
+
+/**
+ * nes_netdev_change_mtu
+ */
+static int nes_netdev_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ int ret = 0;
+ u8 jumbomode=0;
+
+ if ((new_mtu < ETH_ZLEN) || (new_mtu > max_mtu))
+ return -EINVAL;
+
+ netdev->mtu = new_mtu;
+ nesvnic->max_frame_size = new_mtu+ETH_HLEN;
+
+ if (netdev->mtu > 1500) {
+ jumbomode=1;
+ }
+ nes_nic_init_timer_defaults(nesdev, jumbomode);
+
+ if (netif_running(netdev)) {
+ nes_netdev_stop(netdev);
+ nes_netdev_open(netdev);
+ }
+
+ return ret;
+}
+
+
+/**
+ * nes_netdev_exit - destroy network device
+ */
+void nes_netdev_exit(struct nes_vnic *nesvnic)
+{
+ struct net_device *netdev = nesvnic->netdev;
+ struct nes_ib_device *nesibdev = nesvnic->nesibdev;
+
+ nes_debug(NES_DBG_SHUTDOWN, "\n");
+
+ // destroy the ibdevice if RDMA enabled
+ if ((nesvnic->rdma_enabled)&&(nesvnic->of_device_registered)) {
+ nes_destroy_ofa_device( nesibdev );
+ nesvnic->of_device_registered = 0;
+ nesvnic->nesibdev = NULL;
+ }
+ unregister_netdev(netdev);
+ nes_debug(NES_DBG_SHUTDOWN, "\n");
+}
+
+
+#define NES_ETHTOOL_STAT_COUNT 55
+static const char nes_ethtool_stringset[NES_ETHTOOL_STAT_COUNT][ETH_GSTRING_LEN] = {
+ "Link Change Interrupts",
+ "Linearized SKBs",
+ "T/GSO Requests",
+ "Pause Frames Sent",
+ "Pause Frames Received",
+ "Internal Routing Errors",
+ "SQ SW Dropped SKBs",
+ "SQ Locked",
+ "SQ Full",
+ "Segmented TSO Requests",
+ "Rx Symbol Errors",
+ "Rx Jabber Errors",
+ "Rx Oversized Frames",
+ "Rx Short Frames",
+ "Endnode Rx Discards",
+ "Endnode Rx Octets",
+ "Endnode Rx Frames",
+ "Endnode Tx Octets",
+ "Endnode Tx Frames",
+ "mh detected",
+ "mh pauses",
+ "Retransmission Count",
+ "CM Connects",
+ "CM Accepts",
+ "Disconnects",
+ "Connected Events",
+ "Connect Requests",
+ "CM Rejects",
+ "ModifyQP Timeouts",
+ "CreateQPs",
+ "SW DestroyQPs",
+ "DestroyQPs",
+ "CM Closes",
+ "CM Packets Sent",
+ "CM Packets Bounced",
+ "CM Packets Created",
+ "CM Packets Rcvd",
+ "CM Packets Dropped",
+ "CM Packets Retrans",
+ "CM Listens Created",
+ "CM Listens Destroyed",
+ "CM Backlog Drops",
+ "CM Loopbacks",
+ "CM Nodes Created",
+ "CM Nodes Destroyed",
+ "CM Accel Drops",
+ "CM Resets Received",
+ "Timer Inits",
+ "CQ Depth 1",
+ "CQ Depth 4",
+ "CQ Depth 16",
+ "CQ Depth 24",
+ "CQ Depth 32",
+ "CQ Depth 128",
+ "CQ Depth 256",
+};
+
+
+/**
+ * nes_netdev_get_rx_csum
+ */
+static u32 nes_netdev_get_rx_csum (struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+
+ if (nesvnic->rx_checksum_disabled)
+ return 0;
+ else
+ return 1;
+}
+
+
+/**
+ * nes_netdev_set_rc_csum
+ */
+static int nes_netdev_set_rx_csum(struct net_device *netdev, u32 enable)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+
+ if (enable)
+ nesvnic->rx_checksum_disabled = 0;
+ else
+ nesvnic->rx_checksum_disabled = 1;
+ return 0;
+}
+
+
+/**
+ * nes_netdev_get_stats_count
+ */
+static int nes_netdev_get_stats_count(struct net_device *netdev)
+{
+ return NES_ETHTOOL_STAT_COUNT;
+}
+
+
+/**
+ * nes_netdev_get_strings
+ */
+static void nes_netdev_get_strings(struct net_device *netdev, u32 stringset,
+ u8 *ethtool_strings)
+{
+ if (stringset == ETH_SS_STATS)
+ memcpy(ethtool_strings,
+ &nes_ethtool_stringset,
+ sizeof(nes_ethtool_stringset));
+}
+
+
+/**
+ * nes_netdev_get_ethtool_stats
+ */
+static void nes_netdev_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *target_ethtool_stats, u64 *target_stat_values)
+{
+ u64 u64temp;
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ u32 nic_count;
+ u32 u32temp;
+
+ target_ethtool_stats->n_stats = NES_ETHTOOL_STAT_COUNT;
+ target_stat_values[0] = nesvnic->nesdev->link_status_interrupts;
+ target_stat_values[1] = nesvnic->linearized_skbs;
+ target_stat_values[2] = nesvnic->tso_requests;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_TX_PAUSE_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->nesdev->mac_pause_frames_sent += u32temp;
+ target_stat_values[3] = nesvnic->nesdev->mac_pause_frames_sent;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_RX_PAUSE_FRAMES + (nesvnic->nesdev->mac_index*0x200));
+ nesvnic->nesdev->mac_pause_frames_received += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_PORT_RX_DISCARDS + (nesvnic->nesdev->mac_index*0x40));
+ nesvnic->nesdev->port_rx_discards += u32temp;
+ nesvnic->netstats.rx_dropped += u32temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_PORT_TX_DISCARDS + (nesvnic->nesdev->mac_index*0x40));
+ nesvnic->nesdev->port_tx_discards += u32temp;
+ nesvnic->netstats.tx_dropped += u32temp;
+
+ for (nic_count = 0; nic_count < NES_MAX_PORT_COUNT; nic_count++) {
+ if (nesvnic->qp_nic_index[nic_count] == 0xf)
+ break;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_DISCARD +
+ (nesvnic->qp_nic_index[nic_count]*0x200));
+ nesvnic->netstats.rx_dropped += u32temp;
+ nesvnic->endnode_nstat_rx_discard += u32temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_OCTETS_LO +
+ (nesvnic->qp_nic_index[nic_count]*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_OCTETS_HI +
+ (nesvnic->qp_nic_index[nic_count]*0x200))) << 32;
+
+ nesvnic->endnode_nstat_rx_octets += u64temp;
+ nesvnic->netstats.rx_bytes += u64temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_FRAMES_LO +
+ (nesvnic->qp_nic_index[nic_count]*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_RX_FRAMES_HI +
+ (nesvnic->qp_nic_index[nic_count]*0x200))) << 32;
+
+ nesvnic->endnode_nstat_rx_frames += u64temp;
+ nesvnic->netstats.rx_packets += u64temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_OCTETS_LO +
+ (nesvnic->qp_nic_index[nic_count]*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_OCTETS_HI +
+ (nesvnic->qp_nic_index[nic_count]*0x200))) << 32;
+
+ nesvnic->endnode_nstat_tx_octets += u64temp;
+ nesvnic->netstats.tx_bytes += u64temp;
+
+ u64temp = (u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_FRAMES_LO +
+ (nesvnic->qp_nic_index[nic_count]*0x200));
+ u64temp += ((u64)nes_read_indexed(nesdev,
+ NES_IDX_ENDNODE0_NSTAT_TX_FRAMES_HI +
+ (nesvnic->qp_nic_index[nic_count]*0x200))) << 32;
+
+ nesvnic->endnode_nstat_tx_frames += u64temp;
+ nesvnic->netstats.tx_packets += u64temp;
+
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_IPV4_TCP_REXMITS + (nesvnic->qp_nic_index[nic_count]*0x200));
+ nesvnic->endnode_ipv4_tcp_retransmits += u32temp;
+ }
+
+ target_stat_values[4] = nesvnic->nesdev->mac_pause_frames_received;
+ target_stat_values[5] = nesdev->nesadapter->nic_rx_eth_route_err;
+ target_stat_values[6] = nesvnic->tx_sw_dropped;
+ target_stat_values[7] = nesvnic->sq_locked;
+ target_stat_values[8] = nesvnic->sq_full;
+ target_stat_values[9] = nesvnic->segmented_tso_requests;
+ target_stat_values[10] = nesvnic->nesdev->mac_rx_symbol_err_frames;
+ target_stat_values[11] = nesvnic->nesdev->mac_rx_jabber_frames;
+ target_stat_values[12] = nesvnic->nesdev->mac_rx_oversized_frames;
+ target_stat_values[13] = nesvnic->nesdev->mac_rx_short_frames;
+ target_stat_values[14] = nesvnic->endnode_nstat_rx_discard;
+ target_stat_values[15] = nesvnic->endnode_nstat_rx_octets;
+ target_stat_values[16] = nesvnic->endnode_nstat_rx_frames;
+ target_stat_values[17] = nesvnic->endnode_nstat_tx_octets;
+ target_stat_values[18] = nesvnic->endnode_nstat_tx_frames;
+ target_stat_values[19] = mh_detected;
+ target_stat_values[20] = mh_pauses_sent;
+ target_stat_values[21] = nesvnic->endnode_ipv4_tcp_retransmits;
+ target_stat_values[22] = atomic_read(&cm_connects);
+ target_stat_values[23] = atomic_read(&cm_accepts);
+ target_stat_values[24] = atomic_read(&cm_disconnects);
+ target_stat_values[25] = atomic_read(&cm_connecteds);
+ target_stat_values[26] = atomic_read(&cm_connect_reqs);
+ target_stat_values[27] = atomic_read(&cm_rejects);
+ target_stat_values[28] = atomic_read(&mod_qp_timouts);
+ target_stat_values[29] = atomic_read(&qps_created);
+ target_stat_values[30] = atomic_read(&sw_qps_destroyed);
+ target_stat_values[31] = atomic_read(&qps_destroyed);
+ target_stat_values[32] = atomic_read(&cm_closes);
+ target_stat_values[33] = cm_packets_sent;
+ target_stat_values[34] = cm_packets_bounced;
+ target_stat_values[35] = cm_packets_created;
+ target_stat_values[36] = cm_packets_received;
+ target_stat_values[37] = cm_packets_dropped;
+ target_stat_values[38] = cm_packets_retrans;
+ target_stat_values[39] = cm_listens_created;
+ target_stat_values[40] = cm_listens_destroyed;
+ target_stat_values[41] = cm_backlog_drops;
+ target_stat_values[42] = atomic_read(&cm_loopbacks);
+ target_stat_values[43] = atomic_read(&cm_nodes_created);
+ target_stat_values[44] = atomic_read(&cm_nodes_destroyed);
+ target_stat_values[45] = atomic_read(&cm_accel_dropped_pkts);
+ target_stat_values[46] = atomic_read(&cm_resets_recvd);
+ target_stat_values[47] = int_mod_timer_init;
+ target_stat_values[48] = int_mod_cq_depth_1;
+ target_stat_values[49] = int_mod_cq_depth_4;
+ target_stat_values[50] = int_mod_cq_depth_16;
+ target_stat_values[51] = int_mod_cq_depth_24;
+ target_stat_values[52] = int_mod_cq_depth_32;
+ target_stat_values[53] = int_mod_cq_depth_128;
+ target_stat_values[54] = int_mod_cq_depth_256;
+
+}
+
+
+/**
+ * nes_netdev_get_drvinfo
+ */
+static void nes_netdev_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+
+ strcpy(drvinfo->driver, DRV_NAME);
+ strcpy(drvinfo->bus_info, pci_name(nesvnic->nesdev->pcidev));
+ strcpy(drvinfo->fw_version, "TBD");
+ strcpy(drvinfo->version, DRV_VERSION);
+ drvinfo->n_stats = nes_netdev_get_stats_count(netdev);
+ drvinfo->testinfo_len = 0;
+ drvinfo->eedump_len = 0;
+ drvinfo->regdump_len = 0;
+}
+
+
+/**
+ * nes_netdev_set_coalesce
+ */
+static int nes_netdev_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *et_coalesce)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_tune_timer *shared_timer = &nesadapter->tune_timer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nesadapter->periodic_timer_lock, flags);
+ if (et_coalesce->rx_max_coalesced_frames_low) {
+ shared_timer->threshold_low = et_coalesce->rx_max_coalesced_frames_low;
+ }
+ if (et_coalesce->rx_max_coalesced_frames_irq) {
+ shared_timer->threshold_target = et_coalesce->rx_max_coalesced_frames_irq;
+ }
+ if (et_coalesce->rx_max_coalesced_frames_high) {
+ shared_timer->threshold_high = et_coalesce->rx_max_coalesced_frames_high;
+ }
+ if (et_coalesce->rx_coalesce_usecs_low) {
+ shared_timer->timer_in_use_min = et_coalesce->rx_coalesce_usecs_low;
+ }
+ if (et_coalesce->rx_coalesce_usecs_high) {
+ shared_timer->timer_in_use_max = et_coalesce->rx_coalesce_usecs_high;
+ }
+ spin_unlock_irqrestore(&nesadapter->periodic_timer_lock, flags);
+
+ /* using this to drive total interrupt moderation */
+ nesadapter->et_rx_coalesce_usecs_irq = et_coalesce->rx_coalesce_usecs_irq;
+ if (et_coalesce->use_adaptive_rx_coalesce) {
+ nesadapter->et_use_adaptive_rx_coalesce = 1;
+ nesadapter->timer_int_limit = NES_TIMER_INT_LIMIT_DYNAMIC;
+ nesadapter->et_rx_coalesce_usecs_irq = 0;
+ if (et_coalesce->pkt_rate_low) {
+ nesadapter->et_pkt_rate_low = et_coalesce->pkt_rate_low;
+ }
+ } else {
+ nesadapter->et_use_adaptive_rx_coalesce = 0;
+ nesadapter->timer_int_limit = NES_TIMER_INT_LIMIT;
+ if (nesadapter->et_rx_coalesce_usecs_irq) {
+ nes_write32(nesdev->regs+NES_PERIODIC_CONTROL,
+ 0x80000000 | ((u32)(nesadapter->et_rx_coalesce_usecs_irq*8)));
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * nes_netdev_get_coalesce
+ */
+static int nes_netdev_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *et_coalesce)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct ethtool_coalesce temp_et_coalesce;
+ struct nes_hw_tune_timer *shared_timer = &nesadapter->tune_timer;
+ unsigned long flags;
+
+ memset(&temp_et_coalesce, 0, sizeof(temp_et_coalesce));
+ temp_et_coalesce.rx_coalesce_usecs_irq = nesadapter->et_rx_coalesce_usecs_irq;
+ temp_et_coalesce.use_adaptive_rx_coalesce = nesadapter->et_use_adaptive_rx_coalesce;
+ temp_et_coalesce.rate_sample_interval = nesadapter->et_rate_sample_interval;
+ temp_et_coalesce.pkt_rate_low = nesadapter->et_pkt_rate_low;
+ spin_lock_irqsave(&nesadapter->periodic_timer_lock, flags);
+ temp_et_coalesce.rx_max_coalesced_frames_low = shared_timer->threshold_low;
+ temp_et_coalesce.rx_max_coalesced_frames_irq = shared_timer->threshold_target;
+ temp_et_coalesce.rx_max_coalesced_frames_high = shared_timer->threshold_high;
+ temp_et_coalesce.rx_coalesce_usecs_low = shared_timer->timer_in_use_min;
+ temp_et_coalesce.rx_coalesce_usecs_high = shared_timer->timer_in_use_max;
+ if (nesadapter->et_use_adaptive_rx_coalesce) {
+ temp_et_coalesce.rx_coalesce_usecs_irq = shared_timer->timer_in_use;
+ }
+ spin_unlock_irqrestore(&nesadapter->periodic_timer_lock, flags);
+ memcpy(et_coalesce, &temp_et_coalesce, sizeof(*et_coalesce));
+ return 0;
+}
+
+
+/**
+ * nes_netdev_get_pauseparam
+ */
+static void nes_netdev_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *et_pauseparam)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+
+ et_pauseparam->autoneg = 0;
+ et_pauseparam->rx_pause = (nesvnic->nesdev->disable_rx_flow_control == 0) ? 1:0;
+ et_pauseparam->tx_pause = (nesvnic->nesdev->disable_tx_flow_control == 0) ? 1:0;
+}
+
+
+/**
+ * nes_netdev_set_pauseparam
+ */
+static int nes_netdev_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *et_pauseparam)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ u32 u32temp;
+
+ if (et_pauseparam->autoneg) {
+ /* TODO: should return unsupported */
+ return 0;
+ }
+ if ((et_pauseparam->tx_pause == 1) && (nesdev->disable_tx_flow_control == 1)) {
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_TX_CONFIG + (nesdev->mac_index*0x200));
+ u32temp |= NES_IDX_MAC_TX_CONFIG_ENABLE_PAUSE;
+ nes_write_indexed(nesdev,
+ NES_IDX_MAC_TX_CONFIG_ENABLE_PAUSE + (nesdev->mac_index*0x200), u32temp);
+ nesdev->disable_tx_flow_control = 0;
+ } else if ((et_pauseparam->tx_pause == 0) && (nesdev->disable_tx_flow_control == 0)) {
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MAC_TX_CONFIG + (nesdev->mac_index*0x200));
+ u32temp &= ~NES_IDX_MAC_TX_CONFIG_ENABLE_PAUSE;
+ nes_write_indexed(nesdev,
+ NES_IDX_MAC_TX_CONFIG_ENABLE_PAUSE + (nesdev->mac_index*0x200), u32temp);
+ nesdev->disable_tx_flow_control = 1;
+ }
+ if ((et_pauseparam->rx_pause == 1) && (nesdev->disable_rx_flow_control == 1)) {
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MPP_DEBUG + (nesdev->mac_index*0x40));
+ u32temp &= ~NES_IDX_MPP_DEBUG_PORT_DISABLE_PAUSE;
+ nes_write_indexed(nesdev,
+ NES_IDX_MPP_DEBUG + (nesdev->mac_index*0x40), u32temp);
+ nesdev->disable_rx_flow_control = 0;
+ } else if ((et_pauseparam->rx_pause == 0) && (nesdev->disable_rx_flow_control == 0)) {
+ u32temp = nes_read_indexed(nesdev,
+ NES_IDX_MPP_DEBUG + (nesdev->mac_index*0x40));
+ u32temp |= NES_IDX_MPP_DEBUG_PORT_DISABLE_PAUSE;
+ nes_write_indexed(nesdev,
+ NES_IDX_MPP_DEBUG + (nesdev->mac_index*0x40), u32temp);
+ nesdev->disable_rx_flow_control = 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * nes_netdev_get_settings
+ */
+static int nes_netdev_get_settings(struct net_device *netdev, struct ethtool_cmd *et_cmd)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u16 phy_data;
+
+ et_cmd->duplex = DUPLEX_FULL;
+ et_cmd->port = PORT_MII;
+ if (nesadapter->OneG_Mode) {
+ et_cmd->supported = SUPPORTED_1000baseT_Full|SUPPORTED_Autoneg;
+ et_cmd->advertising = ADVERTISED_1000baseT_Full|ADVERTISED_Autoneg;
+ et_cmd->speed = SPEED_1000;
+ nes_read_1G_phy_reg(nesdev, 0, nesadapter->phy_index[nesdev->mac_index],
+ &phy_data);
+ if (phy_data&0x1000) {
+ et_cmd->autoneg = AUTONEG_ENABLE;
+ } else {
+ et_cmd->autoneg = AUTONEG_DISABLE;
+ }
+ et_cmd->transceiver = XCVR_EXTERNAL;
+ et_cmd->phy_address = nesadapter->phy_index[nesdev->mac_index];
+ } else {
+ if (nesadapter->phy_type[nesvnic->logical_port] == NES_PHY_TYPE_IRIS) {
+ et_cmd->transceiver = XCVR_EXTERNAL;
+ et_cmd->port = PORT_FIBRE;
+ et_cmd->supported = SUPPORTED_FIBRE;
+ et_cmd->advertising = ADVERTISED_FIBRE;
+ et_cmd->phy_address = nesadapter->phy_index[nesdev->mac_index];
+ } else {
+ et_cmd->transceiver = XCVR_INTERNAL;
+ et_cmd->supported = SUPPORTED_10000baseT_Full;
+ et_cmd->advertising = ADVERTISED_10000baseT_Full;
+ et_cmd->phy_address = nesdev->mac_index;
+ }
+ et_cmd->speed = SPEED_10000;
+ et_cmd->autoneg = AUTONEG_DISABLE;
+ }
+ et_cmd->maxtxpkt = 511;
+ et_cmd->maxrxpkt = 511;
+ return 0;
+}
+
+
+/**
+ * nes_netdev_set_settings
+ */
+static int nes_netdev_set_settings(struct net_device *netdev, struct ethtool_cmd *et_cmd)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u16 phy_data;
+
+ if (nesadapter->OneG_Mode) {
+ nes_read_1G_phy_reg(nesdev, 0, nesadapter->phy_index[nesdev->mac_index],
+ &phy_data);
+ if (et_cmd->autoneg) {
+ /* Turn on Full duplex, Autoneg, and restart autonegotiation */
+ phy_data |= 0x1300;
+ } else {
+ // Turn off autoneg
+ phy_data &= ~0x1000;
+ }
+ nes_write_1G_phy_reg(nesdev, 0, nesadapter->phy_index[nesdev->mac_index],
+ phy_data);
+ }
+
+ return 0;
+}
+
+
+static struct ethtool_ops nes_ethtool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_settings = nes_netdev_get_settings,
+ .set_settings = nes_netdev_set_settings,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .get_rx_csum = nes_netdev_get_rx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .get_strings = nes_netdev_get_strings,
+ .get_stats_count = nes_netdev_get_stats_count,
+ .get_ethtool_stats = nes_netdev_get_ethtool_stats,
+ .get_drvinfo = nes_netdev_get_drvinfo,
+ .get_coalesce = nes_netdev_get_coalesce,
+ .set_coalesce = nes_netdev_set_coalesce,
+ .get_pauseparam = nes_netdev_get_pauseparam,
+ .set_pauseparam = nes_netdev_set_pauseparam,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .set_rx_csum = nes_netdev_set_rx_csum,
+ .set_sg = ethtool_op_set_sg,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+};
+
+
+static void nes_netdev_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ u32 u32temp;
+
+ nesvnic->vlan_grp = grp;
+
+ /* Enable/Disable VLAN Stripping */
+ u32temp = nes_read_indexed(nesdev, NES_IDX_PCIX_DIAG);
+ if (grp)
+ u32temp &= 0xfdffffff;
+ else
+ u32temp |= 0x02000000;
+
+ nes_write_indexed(nesdev, NES_IDX_PCIX_DIAG, u32temp);
+}
+
+
+/**
+ * nes_netdev_init - initialize network device
+ */
+struct net_device *nes_netdev_init(struct nes_device *nesdev,
+ void __iomem *mmio_addr)
+{
+ u64 u64temp;
+ struct nes_vnic *nesvnic = NULL;
+ struct net_device *netdev;
+ struct nic_qp_map *curr_qp_map;
+ u32 u32temp;
+ u16 phy_data;
+ u16 temp_phy_data;
+
+ netdev = alloc_etherdev(sizeof(struct nes_vnic));
+ if (!netdev) {
+ printk(KERN_ERR PFX "nesvnic etherdev alloc failed");
+ return NULL;
+ }
+
+ nes_debug(NES_DBG_INIT, "netdev = %p, %s\n", netdev, netdev->name);
+
+ SET_NETDEV_DEV(netdev, &nesdev->pcidev->dev);
+
+ nesvnic = netdev_priv(netdev);
+ memset(nesvnic, 0, sizeof(*nesvnic));
+
+ netdev->open = nes_netdev_open;
+ netdev->stop = nes_netdev_stop;
+ netdev->hard_start_xmit = nes_netdev_start_xmit;
+ netdev->get_stats = nes_netdev_get_stats;
+ netdev->tx_timeout = nes_netdev_tx_timeout;
+ netdev->set_mac_address = nes_netdev_set_mac_address;
+ netdev->set_multicast_list = nes_netdev_set_multicast_list;
+ netdev->change_mtu = nes_netdev_change_mtu;
+ netdev->watchdog_timeo = NES_TX_TIMEOUT;
+ netdev->irq = nesdev->pcidev->irq;
+ netdev->mtu = ETH_DATA_LEN;
+ netdev->hard_header_len = ETH_HLEN;
+ netdev->addr_len = ETH_ALEN;
+ netdev->type = ARPHRD_ETHER;
+ netdev->features = NETIF_F_HIGHDMA;
+ netdev->ethtool_ops = &nes_ethtool_ops;
+ netif_napi_add(netdev, &nesvnic->napi, nes_netdev_poll, 128);
+ nes_debug(NES_DBG_INIT, "Enabling VLAN Insert/Delete.\n");
+ netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ netdev->vlan_rx_register = nes_netdev_vlan_rx_register;
+ netdev->features |= NETIF_F_LLTX;
+
+ /* Fill in the port structure */
+ nesvnic->netdev = netdev;
+ nesvnic->nesdev = nesdev;
+ nesvnic->msg_enable = netif_msg_init(debug, default_msg);
+ nesvnic->netdev_index = nesdev->netdev_count;
+ nesvnic->perfect_filter_index = nesdev->nesadapter->netdev_count;
+ nesvnic->max_frame_size = netdev->mtu+netdev->hard_header_len;
+
+ curr_qp_map = nic_qp_mapping_per_function[PCI_FUNC(nesdev->pcidev->devfn)];
+ nesvnic->nic.qp_id = curr_qp_map[nesdev->netdev_count].qpid;
+ nesvnic->nic_index = curr_qp_map[nesdev->netdev_count].nic_index;
+ nesvnic->logical_port = curr_qp_map[nesdev->netdev_count].logical_port;
+
+ /* Setup the burned in MAC address */
+ u64temp = (u64)nesdev->nesadapter->mac_addr_low;
+ u64temp += ((u64)nesdev->nesadapter->mac_addr_high) << 32;
+ u64temp += nesvnic->nic_index;
+ netdev->dev_addr[0] = (u8)(u64temp>>40);
+ netdev->dev_addr[1] = (u8)(u64temp>>32);
+ netdev->dev_addr[2] = (u8)(u64temp>>24);
+ netdev->dev_addr[3] = (u8)(u64temp>>16);
+ netdev->dev_addr[4] = (u8)(u64temp>>8);
+ netdev->dev_addr[5] = (u8)u64temp;
+ memcpy(netdev->perm_addr, netdev->dev_addr, 6);
+
+ if ((nesvnic->logical_port < 2) || (nesdev->nesadapter->hw_rev != NE020_REV)) {
+ netdev->features |= NETIF_F_TSO | NETIF_F_SG | NETIF_F_IP_CSUM;
+ netdev->features |= NETIF_F_GSO | NETIF_F_TSO | NETIF_F_SG | NETIF_F_IP_CSUM;
+ } else {
+ netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+ }
+
+ nes_debug(NES_DBG_INIT, "nesvnic = %p, reported features = 0x%lX, QPid = %d,"
+ " nic_index = %d, logical_port = %d, mac_index = %d.\n",
+ nesvnic, (unsigned long)netdev->features, nesvnic->nic.qp_id,
+ nesvnic->nic_index, nesvnic->logical_port, nesdev->mac_index);
+
+ if (nesvnic->nesdev->nesadapter->port_count == 1) {
+ nesvnic->qp_nic_index[0] = nesvnic->nic_index;
+ nesvnic->qp_nic_index[1] = nesvnic->nic_index + 1;
+ if (nes_drv_opt & NES_DRV_OPT_DUAL_LOGICAL_PORT) {
+ nesvnic->qp_nic_index[2] = 0xf;
+ nesvnic->qp_nic_index[3] = 0xf;
+ } else {
+ nesvnic->qp_nic_index[2] = nesvnic->nic_index + 2;
+ nesvnic->qp_nic_index[3] = nesvnic->nic_index + 3;
+ }
+ } else {
+ if (nesvnic->nesdev->nesadapter->port_count == 2) {
+ nesvnic->qp_nic_index[0] = nesvnic->nic_index;
+ nesvnic->qp_nic_index[1] = nesvnic->nic_index + 2;
+ nesvnic->qp_nic_index[2] = 0xf;
+ nesvnic->qp_nic_index[3] = 0xf;
+ } else {
+ nesvnic->qp_nic_index[0] = nesvnic->nic_index;
+ nesvnic->qp_nic_index[1] = 0xf;
+ nesvnic->qp_nic_index[2] = 0xf;
+ nesvnic->qp_nic_index[3] = 0xf;
+ }
+ }
+ nesvnic->next_qp_nic_index = 0;
+
+ if (nesdev->netdev_count == 0) {
+ nesvnic->rdma_enabled = 1;
+ } else {
+ nesvnic->rdma_enabled = 0;
+ }
+ nesvnic->nic_cq.cq_number = nesvnic->nic.qp_id;
+ spin_lock_init(&nesvnic->tx_lock);
+ nesdev->netdev[nesdev->netdev_count] = netdev;
+
+ nes_debug(NES_DBG_INIT, "Adding nesvnic (%p) to the adapters nesvnic_list for MAC%d.\n",
+ nesvnic, nesdev->mac_index);
+ list_add_tail(&nesvnic->list, &nesdev->nesadapter->nesvnic_list[nesdev->mac_index]);
+
+ if ((nesdev->netdev_count == 0) &&
+ (PCI_FUNC(nesdev->pcidev->devfn) == nesdev->mac_index)) {
+ nes_debug(NES_DBG_INIT, "Setting up PHY interrupt mask. Using register index 0x%04X\n",
+ NES_IDX_PHY_PCS_CONTROL_STATUS0+(0x200*(nesvnic->logical_port&1)));
+ u32temp = nes_read_indexed(nesdev, NES_IDX_PHY_PCS_CONTROL_STATUS0 +
+ (0x200*(nesvnic->logical_port&1)));
+ u32temp |= 0x00200000;
+ nes_write_indexed(nesdev, NES_IDX_PHY_PCS_CONTROL_STATUS0 +
+ (0x200*(nesvnic->logical_port&1)), u32temp);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_PHY_PCS_CONTROL_STATUS0 +
+ (0x200*(nesvnic->logical_port&1)) );
+ if ((u32temp&0x0f1f0000) == 0x0f0f0000) {
+ if (nesdev->nesadapter->phy_type[nesvnic->logical_port] == NES_PHY_TYPE_IRIS) {
+ nes_init_phy(nesdev);
+ nes_read_10G_phy_reg(nesdev, 1,
+ nesdev->nesadapter->phy_index[nesvnic->logical_port]);
+ temp_phy_data = (u16)nes_read_indexed(nesdev,
+ NES_IDX_MAC_MDIO_CONTROL);
+ u32temp = 20;
+ do {
+ nes_read_10G_phy_reg(nesdev, 1,
+ nesdev->nesadapter->phy_index[nesvnic->logical_port]);
+ phy_data = (u16)nes_read_indexed(nesdev,
+ NES_IDX_MAC_MDIO_CONTROL);
+ if ((phy_data == temp_phy_data) || (!(--u32temp)))
+ break;
+ temp_phy_data = phy_data;
+ } while (1);
+ if (phy_data & 4) {
+ nes_debug(NES_DBG_INIT, "The Link is UP!!.\n");
+ nesvnic->linkup = 1;
+ } else {
+ nes_debug(NES_DBG_INIT, "The Link is DOWN!!.\n");
+ }
+ } else {
+ nes_debug(NES_DBG_INIT, "The Link is UP!!.\n");
+ nesvnic->linkup = 1;
+ }
+ }
+ nes_debug(NES_DBG_INIT, "Setting up MAC interrupt mask.\n");
+ /* clear the MAC interrupt status, assumes direct logical to physical mapping */
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS+(0x200*nesvnic->logical_port));
+ nes_debug(NES_DBG_INIT, "Phy interrupt status = 0x%X.\n", u32temp);
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS+(0x200*nesvnic->logical_port), u32temp);
+
+ if (nesdev->nesadapter->phy_type[nesvnic->logical_port] != NES_PHY_TYPE_IRIS)
+ nes_init_phy(nesdev);
+
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_MASK+(0x200*nesvnic->logical_port),
+ ~(NES_MAC_INT_LINK_STAT_CHG | NES_MAC_INT_XGMII_EXT |
+ NES_MAC_INT_TX_UNDERFLOW | NES_MAC_INT_TX_ERROR));
+ }
+
+ return netdev;
+}
+
+
+/**
+ * nes_netdev_destroy - destroy network device structure
+ */
+void nes_netdev_destroy(struct net_device *netdev)
+{
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+
+ /* make sure 'stop' method is called by Linux stack */
+ /* nes_netdev_stop(netdev); */
+
+ list_del(&nesvnic->list);
+
+ if (nesvnic->of_device_registered) {
+ nes_destroy_ofa_device(nesvnic->nesibdev);
+ }
+
+ free_netdev(netdev);
+}
+
+
+/**
+ * nes_nic_cm_xmit -- CM calls this to send out pkts
+ */
+int nes_nic_cm_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ int ret;
+
+ skb->dev = netdev;
+ ret = dev_queue_xmit(skb);
+ if (ret) {
+ nes_debug(NES_DBG_CM, "Bad return code from dev_queue_xmit %d\n", ret);
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect. All rights reserved.
+ * Copyright (c) 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005 Cisco Systems. All rights reserved.
+ * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef NES_USER_H
+#define NES_USER_H
+
+#include <linux/types.h>
+
+#define NES_ABI_USERSPACE_VER 1
+#define NES_ABI_KERNEL_VER 1
+
+/*
+ * Make sure that all structs defined in this file remain laid out so
+ * that they pack the same way on 32-bit and 64-bit architectures (to
+ * avoid incompatibility between 32-bit userspace and 64-bit kernels).
+ * In particular do not use pointer types -- pass pointers in __u64
+ * instead.
+ */
+
+struct nes_alloc_ucontext_req {
+ __u32 reserved32;
+ __u8 userspace_ver;
+ __u8 reserved8[3];
+};
+
+struct nes_alloc_ucontext_resp {
+ __u32 max_pds; /* maximum pds allowed for this user process */
+ __u32 max_qps; /* maximum qps allowed for this user process */
+ __u32 wq_size; /* size of the WQs (sq+rq) allocated to the mmaped area */
+ __u8 virtwq; /* flag to indicate if virtual WQ are to be used or not */
+ __u8 kernel_ver;
+ __u8 reserved[2];
+};
+
+struct nes_alloc_pd_resp {
+ __u32 pd_id;
+ __u32 mmap_db_index;
+};
+
+struct nes_create_cq_req {
+ __u64 user_cq_buffer;
+ __u32 mcrqf;
+ __u8 reserved[4];
+};
+
+struct nes_create_qp_req {
+ __u64 user_wqe_buffers;
+};
+
+enum iwnes_memreg_type {
+ IWNES_MEMREG_TYPE_MEM = 0x0000,
+ IWNES_MEMREG_TYPE_QP = 0x0001,
+ IWNES_MEMREG_TYPE_CQ = 0x0002,
+ IWNES_MEMREG_TYPE_MW = 0x0003,
+ IWNES_MEMREG_TYPE_FMR = 0x0004,
+};
+
+struct nes_mem_reg_req {
+ __u32 reg_type; /* indicates if id is memory, QP or CQ */
+ __u32 reserved;
+};
+
+struct nes_create_cq_resp {
+ __u32 cq_id;
+ __u32 cq_size;
+ __u32 mmap_db_index;
+ __u32 reserved;
+};
+
+struct nes_create_qp_resp {
+ __u32 qp_id;
+ __u32 actual_sq_size;
+ __u32 actual_rq_size;
+ __u32 mmap_sq_db_index;
+ __u32 mmap_rq_db_index;
+ __u32 nes_drv_opt;
+};
+
+#endif /* NES_USER_H */
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+#include "nes.h"
+
+
+
+static u16 nes_read16_eeprom(void __iomem *addr, u16 offset);
+
+u32 mh_detected;
+u32 mh_pauses_sent;
+
+/**
+ * nes_read_eeprom_values -
+ */
+int nes_read_eeprom_values(struct nes_device *nesdev, struct nes_adapter *nesadapter)
+{
+ u32 mac_addr_low;
+ u16 mac_addr_high;
+ u16 eeprom_data;
+ u16 eeprom_offset;
+ u16 next_section_address;
+ u16 sw_section_ver;
+ u8 major_ver = 0;
+ u8 minor_ver = 0;
+
+ /* TODO: deal with EEPROM endian issues */
+ if (nesadapter->firmware_eeprom_offset == 0) {
+ /* Read the EEPROM Parameters */
+ eeprom_data = nes_read16_eeprom(nesdev->regs, 0);
+ nes_debug(NES_DBG_HW, "EEPROM Offset 0 = 0x%04X\n", eeprom_data);
+ eeprom_offset = 2 + (((eeprom_data & 0x007f) << 3) <<
+ ((eeprom_data & 0x0080) >> 7));
+ nes_debug(NES_DBG_HW, "Firmware Offset = 0x%04X\n", eeprom_offset);
+ nesadapter->firmware_eeprom_offset = eeprom_offset;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 4);
+ if (eeprom_data != 0x5746) {
+ nes_debug(NES_DBG_HW, "Not a valid Firmware Image = 0x%04X\n", eeprom_data);
+ return -1;
+ }
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ eeprom_offset += ((eeprom_data & 0x00ff) << 3) << ((eeprom_data & 0x0100) >> 8);
+ nes_debug(NES_DBG_HW, "Software Offset = 0x%04X\n", eeprom_offset);
+ nesadapter->software_eeprom_offset = eeprom_offset;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 4);
+ if (eeprom_data != 0x5753) {
+ printk("Not a valid Software Image = 0x%04X\n", eeprom_data);
+ return -1;
+ }
+ sw_section_ver = nes_read16_eeprom(nesdev->regs, nesadapter->software_eeprom_offset + 6);
+ nes_debug(NES_DBG_HW, "Software section version number = 0x%04X\n",
+ sw_section_ver);
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u (next section) = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ next_section_address = eeprom_offset + (((eeprom_data & 0x00ff) << 3) <<
+ ((eeprom_data & 0x0100) >> 8));
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 4);
+ if (eeprom_data != 0x414d) {
+ nes_debug(NES_DBG_HW, "EEPROM Changed offset should be 0x414d but was 0x%04X\n",
+ eeprom_data);
+ goto no_fw_rev;
+ }
+ eeprom_offset = next_section_address;
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u (next section) = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ next_section_address = eeprom_offset + (((eeprom_data & 0x00ff) << 3) <<
+ ((eeprom_data & 0x0100) >> 8));
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 4);
+ if (eeprom_data != 0x4f52) {
+ nes_debug(NES_DBG_HW, "EEPROM Changed offset should be 0x4f52 but was 0x%04X\n",
+ eeprom_data);
+ goto no_fw_rev;
+ }
+ eeprom_offset = next_section_address;
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u (next section) = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ next_section_address = eeprom_offset + ((eeprom_data & 0x00ff) << 3);
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 4);
+ if (eeprom_data != 0x5746) {
+ nes_debug(NES_DBG_HW, "EEPROM Changed offset should be 0x5746 but was 0x%04X\n",
+ eeprom_data);
+ goto no_fw_rev;
+ }
+ eeprom_offset = next_section_address;
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u (next section) = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ next_section_address = eeprom_offset + ((eeprom_data & 0x00ff) << 3);
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 4);
+ if (eeprom_data != 0x5753) {
+ nes_debug(NES_DBG_HW, "EEPROM Changed offset should be 0x5753 but was 0x%04X\n",
+ eeprom_data);
+ goto no_fw_rev;
+ }
+ eeprom_offset = next_section_address;
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u (next section) = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ next_section_address = eeprom_offset + ((eeprom_data & 0x00ff) << 3);
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 4);
+ if (eeprom_data != 0x414d) {
+ nes_debug(NES_DBG_HW, "EEPROM Changed offset should be 0x414d but was 0x%04X\n",
+ eeprom_data);
+ goto no_fw_rev;
+ }
+ eeprom_offset = next_section_address;
+
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset + 2);
+ nes_debug(NES_DBG_HW, "EEPROM Offset %u (next section) = 0x%04X\n",
+ eeprom_offset + 2, eeprom_data);
+ next_section_address = eeprom_offset + ((eeprom_data & 0x00ff) << 3);
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 4);
+ if (eeprom_data != 0x464e) {
+ nes_debug(NES_DBG_HW, "EEPROM Changed offset should be 0x464e but was 0x%04X\n",
+ eeprom_data);
+ goto no_fw_rev;
+ }
+ eeprom_data = nes_read16_eeprom(nesdev->regs, next_section_address + 8);
+ printk(PFX "Firmware version %u.%u\n", (u8)(eeprom_data>>8), (u8)eeprom_data);
+ major_ver = (u8)(eeprom_data >> 8);
+ minor_ver = (u8)(eeprom_data);
+
+ if (nes_drv_opt & NES_DRV_OPT_DISABLE_VIRT_WQ) {
+ nes_debug(NES_DBG_HW, "Virtual WQs have been disabled\n");
+ } else if (((major_ver == 2) && (minor_ver > 21)) || ((major_ver > 2) && (major_ver != 255))) {
+ nesadapter->virtwq = 1;
+ }
+ nesadapter->firmware_version = (((u32)(u8)(eeprom_data>>8)) << 16) +
+ (u32)((u8)eeprom_data);
+
+no_fw_rev:
+ /* eeprom is valid */
+ eeprom_offset = nesadapter->software_eeprom_offset;
+ eeprom_offset += 8;
+ nesadapter->netdev_max = (u8)nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ mac_addr_high = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ mac_addr_low = (u32)nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ mac_addr_low <<= 16;
+ mac_addr_low += (u32)nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "Base MAC Address = 0x%04X%08X\n",
+ mac_addr_high, mac_addr_low);
+ nes_debug(NES_DBG_HW, "MAC Address count = %u\n", nesadapter->netdev_max);
+
+ nesadapter->mac_addr_low = mac_addr_low;
+ nesadapter->mac_addr_high = mac_addr_high;
+
+ /* Read the Phy Type array */
+ eeprom_offset += 10;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->phy_type[0] = (u8)(eeprom_data >> 8);
+ nesadapter->phy_type[1] = (u8)eeprom_data;
+
+ /* Read the port array */
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->phy_type[2] = (u8)(eeprom_data >> 8);
+ nesadapter->phy_type[3] = (u8)eeprom_data;
+ /* port_count is set by soft reset reg */
+ nes_debug(NES_DBG_HW, "port_count = %u, port 0 -> %u, port 1 -> %u,"
+ " port 2 -> %u, port 3 -> %u\n",
+ nesadapter->port_count,
+ nesadapter->phy_type[0], nesadapter->phy_type[1],
+ nesadapter->phy_type[2], nesadapter->phy_type[3]);
+
+ /* Read PD config array */
+ eeprom_offset += 10;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_size[0] = eeprom_data;
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_base[0] = eeprom_data;
+ nes_debug(NES_DBG_HW, "PD0 config, size=0x%04x, base=0x%04x\n",
+ nesadapter->pd_config_size[0], nesadapter->pd_config_base[0]);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_size[1] = eeprom_data;
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_base[1] = eeprom_data;
+ nes_debug(NES_DBG_HW, "PD1 config, size=0x%04x, base=0x%04x\n",
+ nesadapter->pd_config_size[1], nesadapter->pd_config_base[1]);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_size[2] = eeprom_data;
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_base[2] = eeprom_data;
+ nes_debug(NES_DBG_HW, "PD2 config, size=0x%04x, base=0x%04x\n",
+ nesadapter->pd_config_size[2], nesadapter->pd_config_base[2]);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_size[3] = eeprom_data;
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->pd_config_base[3] = eeprom_data;
+ nes_debug(NES_DBG_HW, "PD3 config, size=0x%04x, base=0x%04x\n",
+ nesadapter->pd_config_size[3], nesadapter->pd_config_base[3]);
+
+ /* Read Rx Pool Size */
+ eeprom_offset += 22; /* 46 */
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->rx_pool_size = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "rx_pool_size = 0x%08X\n", nesadapter->rx_pool_size);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->tx_pool_size = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "tx_pool_size = 0x%08X\n", nesadapter->tx_pool_size);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->rx_threshold = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "rx_threshold = 0x%08X\n", nesadapter->rx_threshold);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->tcp_timer_core_clk_divisor = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "tcp_timer_core_clk_divisor = 0x%08X\n",
+ nesadapter->tcp_timer_core_clk_divisor);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->iwarp_config = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "iwarp_config = 0x%08X\n", nesadapter->iwarp_config);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->cm_config = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "cm_config = 0x%08X\n", nesadapter->cm_config);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->sws_timer_config = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "sws_timer_config = 0x%08X\n", nesadapter->sws_timer_config);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->tcp_config1 = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "tcp_config1 = 0x%08X\n", nesadapter->tcp_config1);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->wqm_wat = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "wqm_wat = 0x%08X\n", nesadapter->wqm_wat);
+
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ eeprom_offset += 2;
+ nesadapter->core_clock = (((u32)eeprom_data) << 16) +
+ nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nes_debug(NES_DBG_HW, "core_clock = 0x%08X\n", nesadapter->core_clock);
+
+ if ((sw_section_ver) && (nesadapter->hw_rev != NE020_REV)) {
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->phy_index[0] = (eeprom_data & 0xff00)>>8;
+ nesadapter->phy_index[1] = eeprom_data & 0x00ff;
+ eeprom_offset += 2;
+ eeprom_data = nes_read16_eeprom(nesdev->regs, eeprom_offset);
+ nesadapter->phy_index[2] = (eeprom_data & 0xff00)>>8;
+ nesadapter->phy_index[3] = eeprom_data & 0x00ff;
+ } else {
+ nesadapter->phy_index[0] = 4;
+ nesadapter->phy_index[1] = 5;
+ nesadapter->phy_index[2] = 6;
+ nesadapter->phy_index[3] = 7;
+ }
+ nes_debug(NES_DBG_HW, "Phy address map = 0 > %u, 1 > %u, 2 > %u, 3 > %u\n",
+ nesadapter->phy_index[0],nesadapter->phy_index[1],
+ nesadapter->phy_index[2],nesadapter->phy_index[3]);
+ }
+
+ return 0;
+}
+
+
+/**
+ * nes_read16_eeprom
+ */
+static u16 nes_read16_eeprom(void __iomem *addr, u16 offset)
+{
+ writel(NES_EEPROM_READ_REQUEST + (offset >> 1),
+ (void __iomem *)addr + NES_EEPROM_COMMAND);
+
+ do {
+ } while (readl((void __iomem *)addr + NES_EEPROM_COMMAND) &
+ NES_EEPROM_READ_REQUEST);
+
+ return readw((void __iomem *)addr + NES_EEPROM_DATA);
+}
+
+
+/**
+ * nes_write_1G_phy_reg
+ */
+void nes_write_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u16 data)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 u32temp;
+ u32 counter;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+
+ nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL,
+ 0x50020000 | data | ((u32)phy_reg << 18) | ((u32)phy_addr << 23));
+ for (counter = 0; counter < 100 ; counter++) {
+ udelay(30);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS);
+ if (u32temp & 1) {
+ /* nes_debug(NES_DBG_PHY, "Phy interrupt status = 0x%X.\n", u32temp); */
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS, 1);
+ break;
+ }
+ }
+ if (!(u32temp & 1))
+ nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n",
+ u32temp);
+
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+}
+
+
+/**
+ * nes_read_1G_phy_reg
+ * This routine only issues the read, the data must be read
+ * separately.
+ */
+void nes_read_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u16 *data)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 u32temp;
+ u32 counter;
+ unsigned long flags;
+
+ /* nes_debug(NES_DBG_PHY, "phy addr = %d, mac_index = %d\n",
+ phy_addr, nesdev->mac_index); */
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+
+ nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL,
+ 0x60020000 | ((u32)phy_reg << 18) | ((u32)phy_addr << 23));
+ for (counter = 0; counter < 100 ; counter++) {
+ udelay(30);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS);
+ if (u32temp & 1) {
+ /* nes_debug(NES_DBG_PHY, "Phy interrupt status = 0x%X.\n", u32temp); */
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS, 1);
+ break;
+ }
+ }
+ if (!(u32temp & 1)) {
+ nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n",
+ u32temp);
+ *data = 0xffff;
+ } else {
+ *data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
+ }
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+}
+
+
+/**
+ * nes_write_10G_phy_reg
+ */
+void nes_write_10G_phy_reg(struct nes_device *nesdev, u16 phy_reg,
+ u8 phy_addr, u16 data)
+{
+ u32 dev_addr;
+ u32 port_addr;
+ u32 u32temp;
+ u32 counter;
+
+ dev_addr = 1;
+ port_addr = phy_addr;
+
+ /* set address */
+ nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL,
+ 0x00020000 | (u32)phy_reg | (((u32)dev_addr) << 18) | (((u32)port_addr) << 23));
+ for (counter = 0; counter < 100 ; counter++) {
+ udelay(30);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS);
+ if (u32temp & 1) {
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS, 1);
+ break;
+ }
+ }
+ if (!(u32temp & 1))
+ nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n",
+ u32temp);
+
+ /* set data */
+ nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL,
+ 0x10020000 | (u32)data | (((u32)dev_addr) << 18) | (((u32)port_addr) << 23));
+ for (counter = 0; counter < 100 ; counter++) {
+ udelay(30);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS);
+ if (u32temp & 1) {
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS, 1);
+ break;
+ }
+ }
+ if (!(u32temp & 1))
+ nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n",
+ u32temp);
+}
+
+
+/**
+ * nes_read_10G_phy_reg
+ * This routine only issues the read, the data must be read
+ * separately.
+ */
+void nes_read_10G_phy_reg(struct nes_device *nesdev, u16 phy_reg, u8 phy_addr)
+{
+ u32 dev_addr;
+ u32 port_addr;
+ u32 u32temp;
+ u32 counter;
+
+ dev_addr = 1;
+ port_addr = phy_addr;
+
+ /* set address */
+ nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL,
+ 0x00020000 | (u32)phy_reg | (((u32)dev_addr) << 18) | (((u32)port_addr) << 23));
+ for (counter = 0; counter < 100 ; counter++) {
+ udelay(30);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS);
+ if (u32temp & 1) {
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS, 1);
+ break;
+ }
+ }
+ if (!(u32temp & 1))
+ nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n",
+ u32temp);
+
+ /* issue read */
+ nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL,
+ 0x30020000 | (((u32)dev_addr) << 18) | (((u32)port_addr) << 23));
+ for (counter = 0; counter < 100 ; counter++) {
+ udelay(30);
+ u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS);
+ if (u32temp & 1) {
+ nes_write_indexed(nesdev, NES_IDX_MAC_INT_STATUS, 1);
+ break;
+ }
+ }
+ if (!(u32temp & 1))
+ nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n",
+ u32temp);
+}
+
+
+/**
+ * nes_get_cqp_request
+ */
+struct nes_cqp_request *nes_get_cqp_request(struct nes_device *nesdev)
+{
+ unsigned long flags;
+ struct nes_cqp_request *cqp_request = NULL;
+
+ if (!list_empty(&nesdev->cqp_avail_reqs)) {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ cqp_request = list_entry(nesdev->cqp_avail_reqs.next,
+ struct nes_cqp_request, list);
+ list_del_init(&cqp_request->list);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ } else {
+ cqp_request = kzalloc(sizeof(struct nes_cqp_request), GFP_KERNEL);
+ if (cqp_request) {
+ cqp_request->dynamic = 1;
+ INIT_LIST_HEAD(&cqp_request->list);
+ }
+ }
+
+ if (cqp_request) {
+ init_waitqueue_head(&cqp_request->waitq);
+ cqp_request->waiting = 0;
+ cqp_request->request_done = 0;
+ cqp_request->callback = 0;
+ init_waitqueue_head(&cqp_request->waitq);
+ nes_debug(NES_DBG_CQP, "Got cqp request %p from the available list \n",
+ cqp_request);
+ } else
+ printk(KERN_ERR PFX "%s: Could not allocated a CQP request.\n",
+ __FUNCTION__);
+
+ return cqp_request;
+}
+
+
+/**
+ * nes_post_cqp_request
+ */
+void nes_post_cqp_request(struct nes_device *nesdev,
+ struct nes_cqp_request *cqp_request, int ring_doorbell)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ unsigned long flags;
+ u32 cqp_head;
+ u64 u64temp;
+
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+
+ if (((((nesdev->cqp.sq_tail+(nesdev->cqp.sq_size*2))-nesdev->cqp.sq_head) &
+ (nesdev->cqp.sq_size - 1)) != 1)
+ && (list_empty(&nesdev->cqp_pending_reqs))) {
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));
+ barrier();
+ u64temp = (unsigned long)cqp_request;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_COMP_SCRATCH_LOW_IDX,
+ u64temp);
+ nes_debug(NES_DBG_CQP, "CQP request (opcode 0x%02X), line 1 = 0x%08X put on CQPs SQ,"
+ " request = %p, cqp_head = %u, cqp_tail = %u, cqp_size = %u,"
+ " waiting = %d, refcount = %d.\n",
+ le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f,
+ le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX]), cqp_request,
+ nesdev->cqp.sq_head, nesdev->cqp.sq_tail, nesdev->cqp.sq_size,
+ cqp_request->waiting, atomic_read(&cqp_request->refcount));
+ barrier();
+ if (ring_doorbell) {
+ /* Ring doorbell (1 WQEs) */
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x01800000 | nesdev->cqp.qp_id);
+ }
+
+ barrier();
+ } else {
+ nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X), line 1 = 0x%08X"
+ " put on the pending queue.\n",
+ cqp_request,
+ le32_to_cpu(cqp_request->cqp_wqe.wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f,
+ le32_to_cpu(cqp_request->cqp_wqe.wqe_words[NES_CQP_WQE_ID_IDX]));
+ list_add_tail(&cqp_request->list, &nesdev->cqp_pending_reqs);
+ }
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+
+ return;
+}
+
+
+/**
+ * nes_arp_table
+ */
+int nes_arp_table(struct nes_device *nesdev, u32 ip_addr, u8 *mac_addr, u32 action)
+{
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ int arp_index;
+ int err = 0;
+
+ for (arp_index = 0; (u32) arp_index < nesadapter->arp_table_size; arp_index++) {
+ if (nesadapter->arp_table[arp_index].ip_addr == ip_addr)
+ break;
+ }
+
+ if (action == NES_ARP_ADD) {
+ if (arp_index != nesadapter->arp_table_size) {
+ return -1;
+ }
+
+ arp_index = 0;
+ err = nes_alloc_resource(nesadapter, nesadapter->allocated_arps,
+ nesadapter->arp_table_size, (u32 *)&arp_index, &nesadapter->next_arp_index);
+ if (err) {
+ nes_debug(NES_DBG_NETDEV, "nes_alloc_resource returned error = %u\n", err);
+ return err;
+ }
+ nes_debug(NES_DBG_NETDEV, "ADD, arp_index=%d\n", arp_index);
+
+ nesadapter->arp_table[arp_index].ip_addr = ip_addr;
+ memcpy(nesadapter->arp_table[arp_index].mac_addr, mac_addr, ETH_ALEN);
+ return arp_index;
+ }
+
+ /* DELETE or RESOLVE */
+ if (arp_index == nesadapter->arp_table_size) {
+ nes_debug(NES_DBG_NETDEV, "mac address not in ARP table - cannot delete or resolve\n");
+ return -1;
+ }
+
+ if (action == NES_ARP_RESOLVE) {
+ nes_debug(NES_DBG_NETDEV, "RESOLVE, arp_index=%d\n", arp_index);
+ return arp_index;
+ }
+
+ if (action == NES_ARP_DELETE) {
+ nes_debug(NES_DBG_NETDEV, "DELETE, arp_index=%d\n", arp_index);
+ nesadapter->arp_table[arp_index].ip_addr = 0;
+ memset(nesadapter->arp_table[arp_index].mac_addr, 0x00, ETH_ALEN);
+ nes_free_resource(nesadapter, nesadapter->allocated_arps, arp_index);
+ return arp_index;
+ }
+
+ return -1;
+}
+
+
+/**
+ * nes_mh_fix
+ */
+void nes_mh_fix(unsigned long parm)
+{
+ unsigned long flags;
+ struct nes_device *nesdev = (struct nes_device *)parm;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_vnic *nesvnic;
+ u32 used_chunks_tx;
+ u32 temp_used_chunks_tx;
+ u32 temp_last_used_chunks_tx;
+ u32 used_chunks_mask;
+ u32 mac_tx_frames_low;
+ u32 mac_tx_frames_high;
+ u32 mac_tx_pauses;
+ u32 serdes_status;
+ u32 reset_value;
+ u32 tx_control;
+ u32 tx_config;
+ u32 tx_pause_quanta;
+ u32 rx_control;
+ u32 rx_config;
+ u32 mac_exact_match;
+ u32 mpp_debug;
+ u32 i=0;
+ u32 chunks_tx_progress = 0;
+
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+ if ((nesadapter->mac_sw_state[0] != NES_MAC_SW_IDLE) || (nesadapter->mac_link_down[0])) {
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+ goto no_mh_work;
+ }
+ nesadapter->mac_sw_state[0] = NES_MAC_SW_MH;
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+ do {
+ mac_tx_frames_low = nes_read_indexed(nesdev, NES_IDX_MAC_TX_FRAMES_LOW);
+ mac_tx_frames_high = nes_read_indexed(nesdev, NES_IDX_MAC_TX_FRAMES_HIGH);
+ mac_tx_pauses = nes_read_indexed(nesdev, NES_IDX_MAC_TX_PAUSE_FRAMES);
+ used_chunks_tx = nes_read_indexed(nesdev, NES_IDX_USED_CHUNKS_TX);
+ nesdev->mac_pause_frames_sent += mac_tx_pauses;
+ used_chunks_mask = 0;
+ temp_used_chunks_tx = used_chunks_tx;
+ temp_last_used_chunks_tx = nesdev->last_used_chunks_tx;
+
+ if (nesdev->netdev[0]) {
+ nesvnic = netdev_priv(nesdev->netdev[0]);
+ } else {
+ break;
+ }
+
+ for (i=0; i<4; i++) {
+ used_chunks_mask <<= 8;
+ if (nesvnic->qp_nic_index[i] != 0xff) {
+ used_chunks_mask |= 0xff;
+ if ((temp_used_chunks_tx&0xff)<(temp_last_used_chunks_tx&0xff)) {
+ chunks_tx_progress = 1;
+ }
+ }
+ temp_used_chunks_tx >>= 8;
+ temp_last_used_chunks_tx >>= 8;
+ }
+ if ((mac_tx_frames_low) || (mac_tx_frames_high) ||
+ (!(used_chunks_tx&used_chunks_mask)) ||
+ (!(nesdev->last_used_chunks_tx&used_chunks_mask)) ||
+ (chunks_tx_progress) ) {
+ nesdev->last_used_chunks_tx = used_chunks_tx;
+ break;
+ }
+ nesdev->last_used_chunks_tx = used_chunks_tx;
+ barrier();
+
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONTROL, 0x00000005);
+ mh_pauses_sent++;
+ mac_tx_pauses = nes_read_indexed(nesdev, NES_IDX_MAC_TX_PAUSE_FRAMES);
+ if (mac_tx_pauses) {
+ nesdev->mac_pause_frames_sent += mac_tx_pauses;
+ break;
+ }
+
+ tx_control = nes_read_indexed(nesdev, NES_IDX_MAC_TX_CONTROL);
+ tx_config = nes_read_indexed(nesdev, NES_IDX_MAC_TX_CONFIG);
+ tx_pause_quanta = nes_read_indexed(nesdev, NES_IDX_MAC_TX_PAUSE_QUANTA);
+ rx_control = nes_read_indexed(nesdev, NES_IDX_MAC_RX_CONTROL);
+ rx_config = nes_read_indexed(nesdev, NES_IDX_MAC_RX_CONFIG);
+ mac_exact_match = nes_read_indexed(nesdev, NES_IDX_MAC_EXACT_MATCH_BOTTOM);
+ mpp_debug = nes_read_indexed(nesdev, NES_IDX_MPP_DEBUG);
+
+ /* one last ditch effort to avoid a false positive */
+ mac_tx_pauses = nes_read_indexed(nesdev, NES_IDX_MAC_TX_PAUSE_FRAMES);
+ if (mac_tx_pauses) {
+ nesdev->last_mac_tx_pauses = nesdev->mac_pause_frames_sent;
+ nes_debug(NES_DBG_HW, "failsafe caught slow outbound pause\n");
+ break;
+ }
+ mh_detected++;
+
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONTROL, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONFIG, 0x00000000);
+ reset_value = nes_read32(nesdev->regs+NES_SOFTWARE_RESET);
+
+ nes_write32(nesdev->regs+NES_SOFTWARE_RESET, reset_value | 0x0000001d);
+
+ while (((nes_read32(nesdev->regs+NES_SOFTWARE_RESET)
+ & 0x00000040) != 0x00000040) && (i++ < 5000)) {
+ /* mdelay(1); */
+ }
+
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_CONTROL0, 0x00000008);
+ serdes_status = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_COMMON_STATUS0);
+
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_EMP0, 0x000bdef7);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_TX_DRIVE0, 0x9ce73000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_MODE0, 0x0ff00000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_SIGDET0, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_BYPASS0, 0x00000000);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_LOOPBACK_CONTROL0, 0x00000000);
+ if (nesadapter->OneG_Mode) {
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_EQ_CONTROL0, 0xf0182222);
+ } else {
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_RX_EQ_CONTROL0, 0xf0042222);
+ }
+ serdes_status = nes_read_indexed(nesdev, NES_IDX_ETH_SERDES_RX_EQ_STATUS0);
+ nes_write_indexed(nesdev, NES_IDX_ETH_SERDES_CDR_CONTROL0, 0x000000ff);
+
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONTROL, tx_control);
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONFIG, tx_config);
+ nes_write_indexed(nesdev, NES_IDX_MAC_TX_PAUSE_QUANTA, tx_pause_quanta);
+ nes_write_indexed(nesdev, NES_IDX_MAC_RX_CONTROL, rx_control);
+ nes_write_indexed(nesdev, NES_IDX_MAC_RX_CONFIG, rx_config);
+ nes_write_indexed(nesdev, NES_IDX_MAC_EXACT_MATCH_BOTTOM, mac_exact_match);
+ nes_write_indexed(nesdev, NES_IDX_MPP_DEBUG, mpp_debug);
+
+ } while (0);
+
+ nesadapter->mac_sw_state[0] = NES_MAC_SW_IDLE;
+no_mh_work:
+ nesdev->nesadapter->mh_timer.expires = jiffies + (HZ/5);
+ add_timer(&nesdev->nesadapter->mh_timer);
+}
+
+/**
+ * nes_clc
+ */
+void nes_clc(unsigned long parm)
+{
+ unsigned long flags;
+ struct nes_device *nesdev = (struct nes_device *)parm;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+
+ spin_lock_irqsave(&nesadapter->phy_lock, flags);
+ nesadapter->link_interrupt_count[0] = 0;
+ nesadapter->link_interrupt_count[1] = 0;
+ nesadapter->link_interrupt_count[2] = 0;
+ nesadapter->link_interrupt_count[3] = 0;
+ spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+
+ nesadapter->lc_timer.expires = jiffies + 3600 * HZ; /* 1 hour */
+ add_timer(&nesadapter->lc_timer);
+}
+
+
+/**
+ * nes_dump_mem
+ */
+void nes_dump_mem(unsigned int dump_debug_level, void *addr, int length)
+{
+ char xlate[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f'};
+ char *ptr;
+ char hex_buf[80];
+ char ascii_buf[20];
+ int num_char;
+ int num_ascii;
+ int num_hex;
+
+ if (!(nes_debug_level & dump_debug_level)) {
+ return;
+ }
+
+ ptr = addr;
+ if (length > 0x100) {
+ nes_debug(dump_debug_level, "Length truncated from %x to %x\n", length, 0x100);
+ length = 0x100;
+ }
+ nes_debug(dump_debug_level, "Address=0x%p, length=0x%x (%d)\n", ptr, length, length);
+
+ memset(ascii_buf, 0, 20);
+ memset(hex_buf, 0, 80);
+
+ num_ascii = 0;
+ num_hex = 0;
+ for (num_char = 0; num_char < length; num_char++) {
+ if (num_ascii == 8) {
+ ascii_buf[num_ascii++] = ' ';
+ hex_buf[num_hex++] = '-';
+ hex_buf[num_hex++] = ' ';
+ }
+
+ if (*ptr < 0x20 || *ptr > 0x7e)
+ ascii_buf[num_ascii++] = '.';
+ else
+ ascii_buf[num_ascii++] = *ptr;
+ hex_buf[num_hex++] = xlate[((*ptr & 0xf0) >> 4)];
+ hex_buf[num_hex++] = xlate[*ptr & 0x0f];
+ hex_buf[num_hex++] = ' ';
+ ptr++;
+
+ if (num_ascii >= 17) {
+ /* output line and reset */
+ nes_debug(dump_debug_level, " %s | %s\n", hex_buf, ascii_buf);
+ memset(ascii_buf, 0, 20);
+ memset(hex_buf, 0, 80);
+ num_ascii = 0;
+ num_hex = 0;
+ }
+ }
+
+ /* output the rest */
+ if (num_ascii) {
+ while (num_ascii < 17) {
+ if (num_ascii == 8) {
+ hex_buf[num_hex++] = ' ';
+ hex_buf[num_hex++] = ' ';
+ }
+ hex_buf[num_hex++] = ' ';
+ hex_buf[num_hex++] = ' ';
+ hex_buf[num_hex++] = ' ';
+ num_ascii++;
+ }
+
+ nes_debug(dump_debug_level, " %s | %s\n", hex_buf, ascii_buf);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/random.h>
+#include <linux/highmem.h>
+#include <asm/byteorder.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/iw_cm.h>
+#include <rdma/ib_user_verbs.h>
+
+#include "nes.h"
+
+#include <rdma/ib_umem.h>
+
+atomic_t mod_qp_timouts;
+atomic_t qps_created;
+atomic_t sw_qps_destroyed;
+
+
+/**
+ * nes_alloc_mw
+ */
+static struct ib_mw *nes_alloc_mw(struct ib_pd *ibpd) {
+ unsigned long flags;
+ struct nes_pd *nespd = to_nespd(ibpd);
+ struct nes_vnic *nesvnic = to_nesvnic(ibpd->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_cqp_request *cqp_request;
+ struct nes_mr *nesmr;
+ struct ib_mw *ibmw;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ int ret;
+ u32 stag;
+ u32 stag_index = 0;
+ u32 next_stag_index = 0;
+ u32 driver_key = 0;
+ u8 stag_key = 0;
+
+ get_random_bytes(&next_stag_index, sizeof(next_stag_index));
+ stag_key = (u8)next_stag_index;
+
+ driver_key = 0;
+
+ next_stag_index >>= 8;
+ next_stag_index %= nesadapter->max_mr;
+
+ ret = nes_alloc_resource(nesadapter, nesadapter->allocated_mrs,
+ nesadapter->max_mr, &stag_index, &next_stag_index);
+ if (ret) {
+ return ERR_PTR(ret);
+ }
+
+ nesmr = kzalloc(sizeof(*nesmr), GFP_KERNEL);
+ if (!nesmr) {
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ stag = stag_index << 8;
+ stag |= driver_key;
+ stag += (u32)stag_key;
+
+ nes_debug(NES_DBG_MR, "Registering STag 0x%08X, index = 0x%08X\n",
+ stag, stag_index);
+
+ /* Register the region with the adapter */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ kfree(nesmr);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
+ cpu_to_le32( NES_CQP_ALLOCATE_STAG | NES_CQP_STAG_RIGHTS_REMOTE_READ |
+ NES_CQP_STAG_RIGHTS_REMOTE_WRITE | NES_CQP_STAG_VA_TO |
+ NES_CQP_STAG_REM_ACC_EN);
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_LEN_HIGH_PD_IDX, (nespd->pd_id & 0x00007fff));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_STAG_IDX, stag);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ ret = wait_event_timeout(cqp_request->waitq, (cqp_request->request_done != 0),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_MR, "Register STag 0x%08X completed, wait_event_timeout ret = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ stag, ret, cqp_request->major_code, cqp_request->minor_code);
+ if ((!ret) || (cqp_request->major_code)) {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ kfree(nesmr);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ if (!ret) {
+ return ERR_PTR(-ETIME);
+ } else {
+ return ERR_PTR(-ENOMEM);
+ }
+ } else {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+
+ nesmr->ibmw.rkey = stag;
+ nesmr->mode = IWNES_MEMREG_TYPE_MW;
+ ibmw = &nesmr->ibmw;
+ nesmr->pbl_4k = 0;
+ nesmr->pbls_used = 0;
+
+ return ibmw;
+}
+
+
+/**
+ * nes_dealloc_mw
+ */
+static int nes_dealloc_mw(struct ib_mw *ibmw)
+{
+ struct nes_mr *nesmr = to_nesmw(ibmw);
+ struct nes_vnic *nesvnic = to_nesvnic(ibmw->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ int err = 0;
+ unsigned long flags;
+ int ret;
+
+ /* Deallocate the window with the adapter */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_MR, "Failed to get a cqp_request.\n");
+ return -ENOMEM;
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, NES_CQP_DEALLOCATE_STAG);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_STAG_IDX, ibmw->rkey);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ nes_debug(NES_DBG_MR, "Waiting for deallocate STag 0x%08X to complete.\n",
+ ibmw->rkey);
+ ret = wait_event_timeout(cqp_request->waitq, (0 != cqp_request->request_done),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_MR, "Deallocate STag completed, wait_event_timeout ret = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ ret, cqp_request->major_code, cqp_request->minor_code);
+ if ((!ret) || (cqp_request->major_code)) {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ if (!ret) {
+ err = -ETIME;
+ } else {
+ err = -EIO;
+ }
+ } else {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ (ibmw->rkey & 0x0fffff00) >> 8);
+ kfree(nesmr);
+
+ return err;
+}
+
+
+/**
+ * nes_bind_mw
+ */
+static int nes_bind_mw(struct ib_qp *ibqp, struct ib_mw *ibmw,
+ struct ib_mw_bind *ibmw_bind)
+{
+ u64 u64temp;
+ struct nes_vnic *nesvnic = to_nesvnic(ibqp->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ /* struct nes_mr *nesmr = to_nesmw(ibmw); */
+ struct nes_qp *nesqp = to_nesqp(ibqp);
+ struct nes_hw_qp_wqe *wqe;
+ unsigned long flags = 0;
+ u32 head;
+ u32 wqe_misc = 0;
+ u32 qsize;
+
+ if (nesqp->ibqp_state > IB_QPS_RTS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&nesqp->lock, flags);
+
+ head = nesqp->hwqp.sq_head;
+ qsize = nesqp->hwqp.sq_tail;
+
+ /* Check for SQ overflow */
+ if (((head + (2 * qsize) - nesqp->hwqp.sq_tail) % qsize) == (qsize - 1)) {
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+ return -EINVAL;
+ }
+
+ wqe = &nesqp->hwqp.sq_vbase[head];
+ /* nes_debug(NES_DBG_MR, "processing sq wqe at %p, head = %u.\n", wqe, head); */
+ nes_fill_init_qp_wqe(wqe, nesqp, head);
+ u64temp = ibmw_bind->wr_id;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_SCRATCH_LOW_IDX, u64temp);
+ wqe_misc = NES_IWARP_SQ_OP_BIND;
+
+ wqe_misc |= NES_IWARP_SQ_WQE_LOCAL_FENCE;
+
+ if (ibmw_bind->send_flags & IB_SEND_SIGNALED)
+ wqe_misc |= NES_IWARP_SQ_WQE_SIGNALED_COMPL;
+
+ if (ibmw_bind->mw_access_flags & IB_ACCESS_REMOTE_WRITE) {
+ wqe_misc |= NES_CQP_STAG_RIGHTS_REMOTE_WRITE;
+ }
+ if (ibmw_bind->mw_access_flags & IB_ACCESS_REMOTE_READ) {
+ wqe_misc |= NES_CQP_STAG_RIGHTS_REMOTE_READ;
+ }
+
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_MISC_IDX, wqe_misc);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_BIND_WQE_MR_IDX, ibmw_bind->mr->lkey);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_BIND_WQE_MW_IDX, ibmw->rkey);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_BIND_WQE_LENGTH_LOW_IDX,
+ ibmw_bind->length);
+ wqe->wqe_words[NES_IWARP_SQ_BIND_WQE_LENGTH_HIGH_IDX] = 0;
+ u64temp = (u64)ibmw_bind->addr;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_BIND_WQE_VA_FBO_LOW_IDX, u64temp);
+
+ head++;
+ if (head >= qsize)
+ head = 0;
+
+ nesqp->hwqp.sq_head = head;
+ barrier();
+
+ nes_write32(nesdev->regs+NES_WQE_ALLOC,
+ (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
+
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+
+ return 0;
+}
+
+
+/**
+ * nes_alloc_fmr
+ */
+static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd,
+ int ibmr_access_flags,
+ struct ib_fmr_attr *ibfmr_attr)
+{
+ unsigned long flags;
+ struct nes_pd *nespd = to_nespd(ibpd);
+ struct nes_vnic *nesvnic = to_nesvnic(ibpd->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_fmr *nesfmr;
+ struct nes_cqp_request *cqp_request;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ int ret;
+ u32 stag;
+ u32 stag_index = 0;
+ u32 next_stag_index = 0;
+ u32 driver_key = 0;
+ u32 opcode = 0;
+ u8 stag_key = 0;
+ int i=0;
+ struct nes_vpbl vpbl;
+
+ get_random_bytes(&next_stag_index, sizeof(next_stag_index));
+ stag_key = (u8)next_stag_index;
+
+ driver_key = 0;
+
+ next_stag_index >>= 8;
+ next_stag_index %= nesadapter->max_mr;
+
+ ret = nes_alloc_resource(nesadapter, nesadapter->allocated_mrs,
+ nesadapter->max_mr, &stag_index, &next_stag_index);
+ if (ret) {
+ goto failed_resource_alloc;
+ }
+
+ nesfmr = kzalloc(sizeof(*nesfmr), GFP_KERNEL);
+ if (!nesfmr) {
+ ret = -ENOMEM;
+ goto failed_fmr_alloc;
+ }
+
+ nesfmr->nesmr.mode = IWNES_MEMREG_TYPE_FMR;
+ if (ibfmr_attr->max_pages == 1) {
+ /* use zero length PBL */
+ nesfmr->nesmr.pbl_4k = 0;
+ nesfmr->nesmr.pbls_used = 0;
+ } else if (ibfmr_attr->max_pages <= 32) {
+ /* use PBL 256 */
+ nesfmr->nesmr.pbl_4k = 0;
+ nesfmr->nesmr.pbls_used = 1;
+ } else if (ibfmr_attr->max_pages <= 512) {
+ /* use 4K PBLs */
+ nesfmr->nesmr.pbl_4k = 1;
+ nesfmr->nesmr.pbls_used = 1;
+ } else {
+ /* use two level 4K PBLs */
+ /* add support for two level 256B PBLs */
+ nesfmr->nesmr.pbl_4k = 1;
+ nesfmr->nesmr.pbls_used = 1 + (ibfmr_attr->max_pages >> 9) +
+ ((ibfmr_attr->max_pages & 511) ? 1 : 0);
+ }
+ /* Register the region with the adapter */
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+
+ /* track PBL resources */
+ if (nesfmr->nesmr.pbls_used != 0) {
+ if (nesfmr->nesmr.pbl_4k) {
+ if (nesfmr->nesmr.pbls_used > nesadapter->free_4kpbl) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ ret = -ENOMEM;
+ goto failed_vpbl_alloc;
+ } else {
+ nesadapter->free_4kpbl -= nesfmr->nesmr.pbls_used;
+ }
+ } else {
+ if (nesfmr->nesmr.pbls_used > nesadapter->free_256pbl) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ ret = -ENOMEM;
+ goto failed_vpbl_alloc;
+ } else {
+ nesadapter->free_256pbl -= nesfmr->nesmr.pbls_used;
+ }
+ }
+ }
+
+ /* one level pbl */
+ if (nesfmr->nesmr.pbls_used == 0) {
+ nesfmr->root_vpbl.pbl_vbase = NULL;
+ nes_debug(NES_DBG_MR, "zero level pbl \n");
+ } else if (nesfmr->nesmr.pbls_used == 1) {
+ /* can change it to kmalloc & dma_map_single */
+ nesfmr->root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
+ &nesfmr->root_vpbl.pbl_pbase);
+ if (!nesfmr->root_vpbl.pbl_vbase) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ ret = -ENOMEM;
+ goto failed_vpbl_alloc;
+ }
+ nesfmr->leaf_pbl_cnt = 0;
+ nes_debug(NES_DBG_MR, "one level pbl, root_vpbl.pbl_vbase=%p \n",
+ nesfmr->root_vpbl.pbl_vbase);
+ }
+ /* two level pbl */
+ else {
+ nesfmr->root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 8192,
+ &nesfmr->root_vpbl.pbl_pbase);
+ if (!nesfmr->root_vpbl.pbl_vbase) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ ret = -ENOMEM;
+ goto failed_vpbl_alloc;
+ }
+
+ nesfmr->root_vpbl.leaf_vpbl = kzalloc(sizeof(*nesfmr->root_vpbl.leaf_vpbl)*1024, GFP_KERNEL);
+ if (!nesfmr->root_vpbl.leaf_vpbl) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ ret = -ENOMEM;
+ goto failed_leaf_vpbl_alloc;
+ }
+
+ nesfmr->leaf_pbl_cnt = nesfmr->nesmr.pbls_used-1;
+ nes_debug(NES_DBG_MR, "two level pbl, root_vpbl.pbl_vbase=%p"
+ " leaf_pbl_cnt=%d root_vpbl.leaf_vpbl=%p\n",
+ nesfmr->root_vpbl.pbl_vbase, nesfmr->leaf_pbl_cnt, nesfmr->root_vpbl.leaf_vpbl);
+
+ for (i=0; i<nesfmr->leaf_pbl_cnt; i++)
+ nesfmr->root_vpbl.leaf_vpbl[i].pbl_vbase = NULL;
+
+ for (i=0; i<nesfmr->leaf_pbl_cnt; i++) {
+ vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
+ &vpbl.pbl_pbase);
+
+ if (!vpbl.pbl_vbase) {
+ ret = -ENOMEM;
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ goto failed_leaf_vpbl_pages_alloc;
+ }
+
+ nesfmr->root_vpbl.pbl_vbase[i].pa_low = cpu_to_le32((u32)vpbl.pbl_pbase);
+ nesfmr->root_vpbl.pbl_vbase[i].pa_high = cpu_to_le32((u32)((((u64)vpbl.pbl_pbase)>>32)));
+ nesfmr->root_vpbl.leaf_vpbl[i] = vpbl;
+
+ nes_debug(NES_DBG_MR, "pbase_low=0x%x, pbase_high=0x%x, vpbl=%p\n",
+ nesfmr->root_vpbl.pbl_vbase[i].pa_low,
+ nesfmr->root_vpbl.pbl_vbase[i].pa_high,
+ &nesfmr->root_vpbl.leaf_vpbl[i]);
+ }
+ }
+ nesfmr->ib_qp = NULL;
+ nesfmr->access_rights =0;
+
+ stag = stag_index << 8;
+ stag |= driver_key;
+ stag += (u32)stag_key;
+
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_MR, "Failed to get a cqp_request.\n");
+ ret = -ENOMEM;
+ goto failed_leaf_vpbl_pages_alloc;
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ nes_debug(NES_DBG_MR, "Registering STag 0x%08X, index = 0x%08X\n",
+ stag, stag_index);
+
+ opcode = NES_CQP_ALLOCATE_STAG | NES_CQP_STAG_VA_TO | NES_CQP_STAG_MR;
+
+ if (nesfmr->nesmr.pbl_4k == 1)
+ opcode |= NES_CQP_STAG_PBL_BLK_SIZE;
+
+ if (ibmr_access_flags & IB_ACCESS_REMOTE_WRITE) {
+ opcode |= NES_CQP_STAG_RIGHTS_REMOTE_WRITE |
+ NES_CQP_STAG_RIGHTS_LOCAL_WRITE | NES_CQP_STAG_REM_ACC_EN;
+ nesfmr->access_rights |=
+ NES_CQP_STAG_RIGHTS_REMOTE_WRITE | NES_CQP_STAG_RIGHTS_LOCAL_WRITE |
+ NES_CQP_STAG_REM_ACC_EN;
+ }
+
+ if (ibmr_access_flags & IB_ACCESS_REMOTE_READ) {
+ opcode |= NES_CQP_STAG_RIGHTS_REMOTE_READ |
+ NES_CQP_STAG_RIGHTS_LOCAL_READ | NES_CQP_STAG_REM_ACC_EN;
+ nesfmr->access_rights |=
+ NES_CQP_STAG_RIGHTS_REMOTE_READ | NES_CQP_STAG_RIGHTS_LOCAL_READ |
+ NES_CQP_STAG_REM_ACC_EN;
+ }
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_LEN_HIGH_PD_IDX, (nespd->pd_id & 0x00007fff));
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_STAG_IDX, stag);
+
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_PBL_BLK_COUNT_IDX] =
+ cpu_to_le32((nesfmr->nesmr.pbls_used>1) ?
+ (nesfmr->nesmr.pbls_used-1) : nesfmr->nesmr.pbls_used);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ ret = wait_event_timeout(cqp_request->waitq, (cqp_request->request_done != 0),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_MR, "Register STag 0x%08X completed, wait_event_timeout ret = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ stag, ret, cqp_request->major_code, cqp_request->minor_code);
+
+ if ((!ret) || (cqp_request->major_code)) {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ ret = (!ret) ? -ETIME : -EIO;
+ goto failed_leaf_vpbl_pages_alloc;
+ } else {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+
+ nesfmr->nesmr.ibfmr.lkey = stag;
+ nesfmr->nesmr.ibfmr.rkey = stag;
+ nesfmr->attr = *ibfmr_attr;
+
+ return &nesfmr->nesmr.ibfmr;
+
+ failed_leaf_vpbl_pages_alloc:
+ /* unroll all allocated pages */
+ for (i=0; i<nesfmr->leaf_pbl_cnt; i++) {
+ if (nesfmr->root_vpbl.leaf_vpbl[i].pbl_vbase) {
+ pci_free_consistent(nesdev->pcidev, 4096, nesfmr->root_vpbl.leaf_vpbl[i].pbl_vbase,
+ nesfmr->root_vpbl.leaf_vpbl[i].pbl_pbase);
+ }
+ }
+ if (nesfmr->root_vpbl.leaf_vpbl)
+ kfree(nesfmr->root_vpbl.leaf_vpbl);
+
+ failed_leaf_vpbl_alloc:
+ if (nesfmr->leaf_pbl_cnt == 0) {
+ if (nesfmr->root_vpbl.pbl_vbase)
+ pci_free_consistent(nesdev->pcidev, 4096, nesfmr->root_vpbl.pbl_vbase,
+ nesfmr->root_vpbl.pbl_pbase);
+ } else
+ pci_free_consistent(nesdev->pcidev, 8192, nesfmr->root_vpbl.pbl_vbase,
+ nesfmr->root_vpbl.pbl_pbase);
+
+ failed_vpbl_alloc:
+ kfree(nesfmr);
+
+ failed_fmr_alloc:
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+
+ failed_resource_alloc:
+ return ERR_PTR(ret);
+}
+
+
+/**
+ * nes_dealloc_fmr
+ */
+static int nes_dealloc_fmr(struct ib_fmr *ibfmr)
+{
+ struct nes_mr *nesmr = to_nesmr_from_ibfmr(ibfmr);
+ struct nes_fmr *nesfmr = to_nesfmr(nesmr);
+ struct nes_vnic *nesvnic = to_nesvnic(ibfmr->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_mr temp_nesmr = *nesmr;
+ int i = 0;
+
+ temp_nesmr.ibmw.device = ibfmr->device;
+ temp_nesmr.ibmw.pd = ibfmr->pd;
+ temp_nesmr.ibmw.rkey = ibfmr->rkey;
+ temp_nesmr.ibmw.uobject = NULL;
+
+ /* free the resources */
+ if (nesfmr->leaf_pbl_cnt == 0) {
+ /* single PBL case */
+ if (nesfmr->root_vpbl.pbl_vbase)
+ pci_free_consistent(nesdev->pcidev, 4096, nesfmr->root_vpbl.pbl_vbase,
+ nesfmr->root_vpbl.pbl_pbase);
+ } else {
+ for (i = 0; i < nesfmr->leaf_pbl_cnt; i++) {
+ pci_free_consistent(nesdev->pcidev, 4096, nesfmr->root_vpbl.leaf_vpbl[i].pbl_vbase,
+ nesfmr->root_vpbl.leaf_vpbl[i].pbl_pbase);
+ }
+ kfree(nesfmr->root_vpbl.leaf_vpbl);
+ pci_free_consistent(nesdev->pcidev, 8192, nesfmr->root_vpbl.pbl_vbase,
+ nesfmr->root_vpbl.pbl_pbase);
+ }
+
+ return nes_dealloc_mw(&temp_nesmr.ibmw);
+}
+
+
+/**
+ * nes_map_phys_fmr
+ */
+static int nes_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list,
+ int list_len, u64 iova)
+{
+ return 0;
+}
+
+
+/**
+ * nes_unmap_frm
+ */
+static int nes_unmap_fmr(struct list_head *ibfmr_list)
+{
+ return 0;
+}
+
+
+
+/**
+ * nes_query_device
+ */
+static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
+{
+ struct nes_vnic *nesvnic = to_nesvnic(ibdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_ib_device *nesibdev = nesvnic->nesibdev;
+
+ memset(props, 0, sizeof(*props));
+ memcpy(&props->sys_image_guid, nesvnic->netdev->dev_addr, 6);
+
+ props->fw_ver = nesdev->nesadapter->fw_ver;
+ props->device_cap_flags = nesdev->nesadapter->device_cap_flags;
+ props->vendor_id = nesdev->nesadapter->vendor_id;
+ props->vendor_part_id = nesdev->nesadapter->vendor_part_id;
+ props->hw_ver = nesdev->nesadapter->hw_rev;
+ props->max_mr_size = 0x80000000;
+ props->max_qp = nesibdev->max_qp;
+ props->max_qp_wr = nesdev->nesadapter->max_qp_wr - 2;
+ props->max_sge = nesdev->nesadapter->max_sge;
+ props->max_cq = nesibdev->max_cq;
+ props->max_cqe = nesdev->nesadapter->max_cqe - 1;
+ props->max_mr = nesibdev->max_mr;
+ props->max_mw = nesibdev->max_mr;
+ props->max_pd = nesibdev->max_pd;
+ props->max_sge_rd = 1;
+ switch (nesdev->nesadapter->max_irrq_wr) {
+ case 0:
+ props->max_qp_rd_atom = 1;
+ break;
+ case 1:
+ props->max_qp_rd_atom = 4;
+ break;
+ case 2:
+ props->max_qp_rd_atom = 16;
+ break;
+ case 3:
+ props->max_qp_rd_atom = 32;
+ break;
+ default:
+ props->max_qp_rd_atom = 0;
+ }
+ props->max_qp_init_rd_atom = props->max_qp_wr;
+ props->atomic_cap = IB_ATOMIC_NONE;
+ props->max_map_per_fmr = 1;
+
+ return 0;
+}
+
+
+/**
+ * nes_query_port
+ */
+static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr *props)
+{
+ memset(props, 0, sizeof(*props));
+
+ props->max_mtu = IB_MTU_2048;
+ props->active_mtu = IB_MTU_2048;
+ props->lid = 1;
+ props->lmc = 0;
+ props->sm_lid = 0;
+ props->sm_sl = 0;
+ props->state = IB_PORT_ACTIVE;
+ props->phys_state = 0;
+ props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_REINIT_SUP |
+ IB_PORT_VENDOR_CLASS_SUP | IB_PORT_BOOT_MGMT_SUP;
+ props->gid_tbl_len = 1;
+ props->pkey_tbl_len = 1;
+ props->qkey_viol_cntr = 0;
+ props->active_width = IB_WIDTH_4X;
+ props->active_speed = 1;
+ props->max_msg_sz = 0x80000000;
+
+ return 0;
+}
+
+
+/**
+ * nes_modify_port
+ */
+static int nes_modify_port(struct ib_device *ibdev, u8 port,
+ int port_modify_mask, struct ib_port_modify *props)
+{
+ return 0;
+}
+
+
+/**
+ * nes_query_pkey
+ */
+static int nes_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
+{
+ *pkey = 0;
+ return 0;
+}
+
+
+/**
+ * nes_query_gid
+ */
+static int nes_query_gid(struct ib_device *ibdev, u8 port,
+ int index, union ib_gid *gid)
+{
+ struct nes_vnic *nesvnic = to_nesvnic(ibdev);
+
+ memset(&(gid->raw[0]), 0, sizeof(gid->raw));
+ memcpy(&(gid->raw[0]), nesvnic->netdev->dev_addr, 6);
+
+ return 0;
+}
+
+
+/**
+ * nes_alloc_ucontext - Allocate the user context data structure. This keeps track
+ * of all objects associated with a particular user-mode client.
+ */
+static struct ib_ucontext *nes_alloc_ucontext(struct ib_device *ibdev,
+ struct ib_udata *udata)
+{
+ struct nes_vnic *nesvnic = to_nesvnic(ibdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_alloc_ucontext_req req;
+ struct nes_alloc_ucontext_resp uresp;
+ struct nes_ucontext *nes_ucontext;
+ struct nes_ib_device *nesibdev = nesvnic->nesibdev;
+
+
+ if (ib_copy_from_udata(&req, udata, sizeof(struct nes_alloc_ucontext_req))) {
+ printk(KERN_ERR PFX "Invalid structure size on allocate user context.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (req.userspace_ver != NES_ABI_USERSPACE_VER) {
+ printk(KERN_ERR PFX "Invalid userspace driver version detected. Detected version %d, should be %d\n",
+ req.userspace_ver, NES_ABI_USERSPACE_VER);
+ return ERR_PTR(-EINVAL);
+ }
+
+
+ memset(&uresp, 0, sizeof uresp);
+
+ uresp.max_qps = nesibdev->max_qp;
+ uresp.max_pds = nesibdev->max_pd;
+ uresp.wq_size = nesdev->nesadapter->max_qp_wr * 2;
+ uresp.virtwq = nesadapter->virtwq;
+ uresp.kernel_ver = NES_ABI_KERNEL_VER;
+
+ nes_ucontext = kzalloc(sizeof *nes_ucontext, GFP_KERNEL);
+ if (!nes_ucontext)
+ return ERR_PTR(-ENOMEM);
+
+ nes_ucontext->nesdev = nesdev;
+ nes_ucontext->mmap_wq_offset = uresp.max_pds;
+ nes_ucontext->mmap_cq_offset = nes_ucontext->mmap_wq_offset +
+ ((sizeof(struct nes_hw_qp_wqe) * uresp.max_qps * 2) + PAGE_SIZE-1) /
+ PAGE_SIZE;
+
+
+ if (ib_copy_to_udata(udata, &uresp, sizeof uresp)) {
+ kfree(nes_ucontext);
+ return ERR_PTR(-EFAULT);
+ }
+
+ INIT_LIST_HEAD(&nes_ucontext->cq_reg_mem_list);
+ INIT_LIST_HEAD(&nes_ucontext->qp_reg_mem_list);
+ atomic_set(&nes_ucontext->usecnt, 1);
+ return &nes_ucontext->ibucontext;
+}
+
+
+/**
+ * nes_dealloc_ucontext
+ */
+static int nes_dealloc_ucontext(struct ib_ucontext *context)
+{
+ /* struct nes_vnic *nesvnic = to_nesvnic(context->device); */
+ /* struct nes_device *nesdev = nesvnic->nesdev; */
+ struct nes_ucontext *nes_ucontext = to_nesucontext(context);
+
+ if (!atomic_dec_and_test(&nes_ucontext->usecnt))
+ return 0;
+ kfree(nes_ucontext);
+ return 0;
+}
+
+
+/**
+ * nes_mmap
+ */
+static int nes_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
+{
+ unsigned long index;
+ struct nes_vnic *nesvnic = to_nesvnic(context->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ /* struct nes_adapter *nesadapter = nesdev->nesadapter; */
+ struct nes_ucontext *nes_ucontext;
+ struct nes_qp *nesqp;
+
+ nes_ucontext = to_nesucontext(context);
+
+
+ if (vma->vm_pgoff >= nes_ucontext->mmap_wq_offset) {
+ index = (vma->vm_pgoff - nes_ucontext->mmap_wq_offset) * PAGE_SIZE;
+ index /= ((sizeof(struct nes_hw_qp_wqe) * nesdev->nesadapter->max_qp_wr * 2) +
+ PAGE_SIZE-1) & (~(PAGE_SIZE-1));
+ if (!test_bit(index, nes_ucontext->allocated_wqs)) {
+ nes_debug(NES_DBG_MMAP, "wq %lu not allocated\n", index);
+ return -EFAULT;
+ }
+ nesqp = nes_ucontext->mmap_nesqp[index];
+ if (nesqp == NULL) {
+ nes_debug(NES_DBG_MMAP, "wq %lu has a NULL QP base.\n", index);
+ return -EFAULT;
+ }
+ if (remap_pfn_range(vma, vma->vm_start,
+ virt_to_phys(nesqp->hwqp.sq_vbase) >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) {
+ nes_debug(NES_DBG_MMAP, "remap_pfn_range failed.\n");
+ return -EAGAIN;
+ }
+ vma->vm_private_data = nesqp;
+ return 0;
+ } else {
+ index = vma->vm_pgoff;
+ if (!test_bit(index, nes_ucontext->allocated_doorbells))
+ return -EFAULT;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (io_remap_pfn_range(vma, vma->vm_start,
+ (nesdev->doorbell_start +
+ ((nes_ucontext->mmap_db_index[index] - nesdev->base_doorbell_index) * 4096))
+ >> PAGE_SHIFT, PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+ vma->vm_private_data = nes_ucontext;
+ return 0;
+ }
+
+ return -ENOSYS;
+}
+
+
+/**
+ * nes_alloc_pd
+ */
+static struct ib_pd *nes_alloc_pd(struct ib_device *ibdev,
+ struct ib_ucontext *context, struct ib_udata *udata)
+{
+ struct nes_pd *nespd;
+ struct nes_vnic *nesvnic = to_nesvnic(ibdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_ucontext *nesucontext;
+ struct nes_alloc_pd_resp uresp;
+ u32 pd_num = 0;
+ int err;
+
+ nes_debug(NES_DBG_PD, "nesvnic=%p, netdev=%p %s, ibdev=%p, context=%p, netdev refcnt=%u\n",
+ nesvnic, nesdev->netdev[0], nesdev->netdev[0]->name, ibdev, context,
+ atomic_read(&nesvnic->netdev->refcnt));
+
+ err = nes_alloc_resource(nesadapter, nesadapter->allocated_pds,
+ nesadapter->max_pd, &pd_num, &nesadapter->next_pd);
+ if (err) {
+ return ERR_PTR(err);
+ }
+
+ nespd = kzalloc(sizeof (struct nes_pd), GFP_KERNEL);
+ if (!nespd) {
+ nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nes_debug(NES_DBG_PD, "Allocating PD (%p) for ib device %s\n",
+ nespd, nesvnic->nesibdev->ibdev.name);
+
+ nespd->pd_id = (pd_num << (PAGE_SHIFT-12)) + nesadapter->base_pd;
+
+ if (context) {
+ nesucontext = to_nesucontext(context);
+ nespd->mmap_db_index = find_next_zero_bit(nesucontext->allocated_doorbells,
+ NES_MAX_USER_DB_REGIONS, nesucontext->first_free_db);
+ nes_debug(NES_DBG_PD, "find_first_zero_biton doorbells returned %u, mapping pd_id %u.\n",
+ nespd->mmap_db_index, nespd->pd_id);
+ if (nespd->mmap_db_index > NES_MAX_USER_DB_REGIONS) {
+ nes_debug(NES_DBG_PD, "mmap_db_index > MAX\n");
+ nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num);
+ kfree(nespd);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ uresp.pd_id = nespd->pd_id;
+ uresp.mmap_db_index = nespd->mmap_db_index;
+ if (ib_copy_to_udata(udata, &uresp, sizeof (struct nes_alloc_pd_resp))) {
+ nes_free_resource(nesadapter, nesadapter->allocated_pds, pd_num);
+ kfree(nespd);
+ return ERR_PTR(-EFAULT);
+ }
+
+ set_bit(nespd->mmap_db_index, nesucontext->allocated_doorbells);
+ nesucontext->mmap_db_index[nespd->mmap_db_index] = nespd->pd_id;
+ nesucontext->first_free_db = nespd->mmap_db_index + 1;
+ }
+
+ nes_debug(NES_DBG_PD, "PD%u structure located @%p.\n", nespd->pd_id, nespd);
+ return &nespd->ibpd;
+}
+
+
+/**
+ * nes_dealloc_pd
+ */
+static int nes_dealloc_pd(struct ib_pd *ibpd)
+{
+ struct nes_ucontext *nesucontext;
+ struct nes_pd *nespd = to_nespd(ibpd);
+ struct nes_vnic *nesvnic = to_nesvnic(ibpd->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+
+ if ((ibpd->uobject) && (ibpd->uobject->context)) {
+ nesucontext = to_nesucontext(ibpd->uobject->context);
+ nes_debug(NES_DBG_PD, "Clearing bit %u from allocated doorbells\n",
+ nespd->mmap_db_index);
+ clear_bit(nespd->mmap_db_index, nesucontext->allocated_doorbells);
+ nesucontext->mmap_db_index[nespd->mmap_db_index] = 0;
+ if (nesucontext->first_free_db > nespd->mmap_db_index) {
+ nesucontext->first_free_db = nespd->mmap_db_index;
+ }
+ }
+
+ nes_debug(NES_DBG_PD, "Deallocating PD%u structure located @%p.\n",
+ nespd->pd_id, nespd);
+ nes_free_resource(nesadapter, nesadapter->allocated_pds,
+ (nespd->pd_id-nesadapter->base_pd)>>(PAGE_SHIFT-12));
+ kfree(nespd);
+
+ return 0;
+}
+
+
+/**
+ * nes_create_ah
+ */
+static struct ib_ah *nes_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+
+/**
+ * nes_destroy_ah
+ */
+static int nes_destroy_ah(struct ib_ah *ah)
+{
+ return -ENOSYS;
+}
+
+
+/**
+ * nes_get_encoded_size
+ */
+static inline u8 nes_get_encoded_size(int *size)
+{
+ u8 encoded_size = 0;
+ if (*size <= 32) {
+ *size = 32;
+ encoded_size = 1;
+ } else if (*size <= 128) {
+ *size = 128;
+ encoded_size = 2;
+ } else if (*size <= 512) {
+ *size = 512;
+ encoded_size = 3;
+ }
+ return (encoded_size);
+}
+
+
+
+/**
+ * nes_setup_virt_qp
+ */
+static int nes_setup_virt_qp(struct nes_qp *nesqp, struct nes_pbl *nespbl,
+ struct nes_vnic *nesvnic, int sq_size, int rq_size)
+{
+ unsigned long flags;
+ void *mem;
+ __le64 *pbl = NULL;
+ __le64 *tpbl;
+ __le64 *pblbuffer;
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ u32 pbl_entries;
+ u8 rq_pbl_entries;
+ u8 sq_pbl_entries;
+
+ pbl_entries = nespbl->pbl_size >> 3;
+ nes_debug(NES_DBG_QP, "Userspace PBL, pbl_size=%u, pbl_entries = %d pbl_vbase=%p, pbl_pbase=%p\n",
+ nespbl->pbl_size, pbl_entries,
+ (void *)nespbl->pbl_vbase,
+ (void *)nespbl->pbl_pbase);
+ pbl = (__le64 *) nespbl->pbl_vbase; /* points to first pbl entry */
+ /* now lets set the sq_vbase as well as rq_vbase addrs we will assign */
+ /* the first pbl to be fro the rq_vbase... */
+ rq_pbl_entries = (rq_size * sizeof(struct nes_hw_qp_wqe)) >> 12;
+ sq_pbl_entries = (sq_size * sizeof(struct nes_hw_qp_wqe)) >> 12;
+ nesqp->hwqp.sq_pbase = (le32_to_cpu(((__le32 *)pbl)[0])) | ((u64)((le32_to_cpu(((__le32 *)pbl)[1]))) << 32);
+ if (!nespbl->page) {
+ nes_debug(NES_DBG_QP, "QP nespbl->page is NULL \n");
+ kfree(nespbl);
+ return -ENOMEM;
+ }
+
+ nesqp->hwqp.sq_vbase = kmap(nespbl->page);
+ nesqp->page = nespbl->page;
+ if (!nesqp->hwqp.sq_vbase) {
+ nes_debug(NES_DBG_QP, "QP sq_vbase kmap failed\n");
+ kfree(nespbl);
+ return -ENOMEM;
+ }
+
+ /* Now to get to sq.. we need to calculate how many */
+ /* PBL entries were used by the rq.. */
+ pbl += sq_pbl_entries;
+ nesqp->hwqp.rq_pbase = (le32_to_cpu(((__le32 *)pbl)[0])) | ((u64)((le32_to_cpu(((__le32 *)pbl)[1]))) << 32);
+ /* nesqp->hwqp.rq_vbase = bus_to_virt(*pbl); */
+ /*nesqp->hwqp.rq_vbase = phys_to_virt(*pbl); */
+
+ nes_debug(NES_DBG_QP, "QP sq_vbase= %p sq_pbase=%p rq_vbase=%p rq_pbase=%p\n",
+ nesqp->hwqp.sq_vbase, (void *)nesqp->hwqp.sq_pbase,
+ nesqp->hwqp.rq_vbase, (void *)nesqp->hwqp.rq_pbase);
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ if (!nesadapter->free_256pbl) {
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size, nespbl->pbl_vbase,
+ nespbl->pbl_pbase);
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kunmap(nesqp->page);
+ kfree(nespbl);
+ return -ENOMEM;
+ }
+ nesadapter->free_256pbl--;
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+
+ nesqp->pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 256, &nesqp->pbl_pbase);
+ pblbuffer = nesqp->pbl_vbase;
+ if (!nesqp->pbl_vbase) {
+ /* memory allocated during nes_reg_user_mr() */
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size, nespbl->pbl_vbase,
+ nespbl->pbl_pbase);
+ kfree(nespbl);
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ nesadapter->free_256pbl++;
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kunmap(nesqp->page);
+ return -ENOMEM;
+ }
+ memset(nesqp->pbl_vbase, 0, 256);
+ /* fill in the page address in the pbl buffer.. */
+ tpbl = pblbuffer + 16;
+ pbl = (__le64 *)nespbl->pbl_vbase;
+ while (sq_pbl_entries--)
+ *tpbl++ = *pbl++;
+ tpbl = pblbuffer;
+ while (rq_pbl_entries--)
+ *tpbl++ = *pbl++;
+
+ /* done with memory allocated during nes_reg_user_mr() */
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size, nespbl->pbl_vbase,
+ nespbl->pbl_pbase);
+ kfree(nespbl);
+
+ nesqp->qp_mem_size =
+ max((u32)sizeof(struct nes_qp_context), ((u32)256)) + 256; /* this is Q2 */
+ /* Round up to a multiple of a page */
+ nesqp->qp_mem_size += PAGE_SIZE - 1;
+ nesqp->qp_mem_size &= ~(PAGE_SIZE - 1);
+
+ mem = pci_alloc_consistent(nesdev->pcidev, nesqp->qp_mem_size,
+ &nesqp->hwqp.q2_pbase);
+
+ if (!mem) {
+ pci_free_consistent(nesdev->pcidev, 256, nesqp->pbl_vbase, nesqp->pbl_pbase);
+ nesqp->pbl_vbase = NULL;
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ nesadapter->free_256pbl++;
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kunmap(nesqp->page);
+ return -ENOMEM;
+ }
+ nesqp->hwqp.q2_vbase = mem;
+ mem += 256;
+ memset(nesqp->hwqp.q2_vbase, 0, 256);
+ nesqp->nesqp_context = mem;
+ memset(nesqp->nesqp_context, 0, sizeof(*nesqp->nesqp_context));
+ nesqp->nesqp_context_pbase = nesqp->hwqp.q2_pbase + 256;
+
+ return 0;
+}
+
+
+/**
+ * nes_setup_mmap_qp
+ */
+static int nes_setup_mmap_qp(struct nes_qp *nesqp, struct nes_vnic *nesvnic,
+ int sq_size, int rq_size)
+{
+ void *mem;
+ struct nes_device *nesdev = nesvnic->nesdev;
+
+ nesqp->qp_mem_size = (sizeof(struct nes_hw_qp_wqe) * sq_size) +
+ (sizeof(struct nes_hw_qp_wqe) * rq_size) +
+ max((u32)sizeof(struct nes_qp_context), ((u32)256)) +
+ 256; /* this is Q2 */
+ /* Round up to a multiple of a page */
+ nesqp->qp_mem_size += PAGE_SIZE - 1;
+ nesqp->qp_mem_size &= ~(PAGE_SIZE - 1);
+
+ mem = pci_alloc_consistent(nesdev->pcidev, nesqp->qp_mem_size,
+ &nesqp->hwqp.sq_pbase);
+ if (!mem)
+ return -ENOMEM;
+ nes_debug(NES_DBG_QP, "PCI consistent memory for "
+ "host descriptor rings located @ %p (pa = 0x%08lX.) size = %u.\n",
+ mem, (unsigned long)nesqp->hwqp.sq_pbase, nesqp->qp_mem_size);
+
+ memset(mem, 0, nesqp->qp_mem_size);
+
+ nesqp->hwqp.sq_vbase = mem;
+ mem += sizeof(struct nes_hw_qp_wqe) * sq_size;
+
+ nesqp->hwqp.rq_vbase = mem;
+ nesqp->hwqp.rq_pbase = nesqp->hwqp.sq_pbase +
+ sizeof(struct nes_hw_qp_wqe) * sq_size;
+ mem += sizeof(struct nes_hw_qp_wqe) * rq_size;
+
+ nesqp->hwqp.q2_vbase = mem;
+ nesqp->hwqp.q2_pbase = nesqp->hwqp.rq_pbase +
+ sizeof(struct nes_hw_qp_wqe) * rq_size;
+ mem += 256;
+ memset(nesqp->hwqp.q2_vbase, 0, 256);
+
+ nesqp->nesqp_context = mem;
+ nesqp->nesqp_context_pbase = nesqp->hwqp.q2_pbase + 256;
+ memset(nesqp->nesqp_context, 0, sizeof(*nesqp->nesqp_context));
+ return 0;
+}
+
+
+/**
+ * nes_free_qp_mem() is to free up the qp's pci_alloc_consistent() memory.
+ */
+static inline void nes_free_qp_mem(struct nes_device *nesdev,
+ struct nes_qp *nesqp, int virt_wqs)
+{
+ unsigned long flags;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ if (!virt_wqs) {
+ pci_free_consistent(nesdev->pcidev, nesqp->qp_mem_size,
+ nesqp->hwqp.sq_vbase, nesqp->hwqp.sq_pbase);
+ }else {
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ nesadapter->free_256pbl++;
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ pci_free_consistent(nesdev->pcidev, nesqp->qp_mem_size, nesqp->hwqp.q2_vbase, nesqp->hwqp.q2_pbase);
+ pci_free_consistent(nesdev->pcidev, 256, nesqp->pbl_vbase, nesqp->pbl_pbase );
+ nesqp->pbl_vbase = NULL;
+ kunmap(nesqp->page);
+ }
+}
+
+
+/**
+ * nes_create_qp
+ */
+static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
+ struct ib_qp_init_attr *init_attr, struct ib_udata *udata)
+{
+ u64 u64temp= 0;
+ u64 u64nesqp = 0;
+ struct nes_pd *nespd = to_nespd(ibpd);
+ struct nes_vnic *nesvnic = to_nesvnic(ibpd->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_qp *nesqp;
+ struct nes_cq *nescq;
+ struct nes_ucontext *nes_ucontext;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ struct nes_create_qp_req req;
+ struct nes_create_qp_resp uresp;
+ struct nes_pbl *nespbl = NULL;
+ u32 qp_num = 0;
+ u32 opcode = 0;
+ /* u32 counter = 0; */
+ void *mem;
+ unsigned long flags;
+ int ret;
+ int err;
+ int virt_wqs = 0;
+ int sq_size;
+ int rq_size;
+ u8 sq_encoded_size;
+ u8 rq_encoded_size;
+ /* int counter; */
+
+ atomic_inc(&qps_created);
+ switch (init_attr->qp_type) {
+ case IB_QPT_RC:
+ if (nes_drv_opt & NES_DRV_OPT_NO_INLINE_DATA) {
+ init_attr->cap.max_inline_data = 0;
+ } else {
+ init_attr->cap.max_inline_data = 64;
+ }
+ sq_size = init_attr->cap.max_send_wr;
+ rq_size = init_attr->cap.max_recv_wr;
+
+ // check if the encoded sizes are OK or not...
+ sq_encoded_size = nes_get_encoded_size(&sq_size);
+ rq_encoded_size = nes_get_encoded_size(&rq_size);
+
+ if ((!sq_encoded_size) || (!rq_encoded_size)) {
+ nes_debug(NES_DBG_QP, "ERROR bad rq (%u) or sq (%u) size\n",
+ rq_size, sq_size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ init_attr->cap.max_send_wr = sq_size -2;
+ init_attr->cap.max_recv_wr = rq_size -1;
+ nes_debug(NES_DBG_QP, "RQ size=%u, SQ Size=%u\n", rq_size, sq_size);
+
+ ret = nes_alloc_resource(nesadapter, nesadapter->allocated_qps,
+ nesadapter->max_qp, &qp_num, &nesadapter->next_qp);
+ if (ret) {
+ return ERR_PTR(ret);
+ }
+
+ /* Need 512 (actually now 1024) byte alignment on this structure */
+ mem = kzalloc(sizeof(*nesqp)+NES_SW_CONTEXT_ALIGN-1, GFP_KERNEL);
+ if (!mem) {
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ nes_debug(NES_DBG_QP, "Unable to allocate QP\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ u64nesqp = (unsigned long)mem;
+ u64nesqp += ((u64)NES_SW_CONTEXT_ALIGN) - 1;
+ u64temp = ((u64)NES_SW_CONTEXT_ALIGN) - 1;
+ u64nesqp &= ~u64temp;
+ nesqp = (struct nes_qp *)(unsigned long)u64nesqp;
+ /* nes_debug(NES_DBG_QP, "nesqp=%p, allocated buffer=%p. Rounded to closest %u\n",
+ nesqp, mem, NES_SW_CONTEXT_ALIGN); */
+ nesqp->allocated_buffer = mem;
+
+ if (udata) {
+ if (ib_copy_from_udata(&req, udata, sizeof(struct nes_create_qp_req))) {
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ kfree(nesqp->allocated_buffer);
+ nes_debug(NES_DBG_QP, "ib_copy_from_udata() Failed \n");
+ return NULL;
+ }
+ if (req.user_wqe_buffers) {
+ virt_wqs = 1;
+ }
+ if ((ibpd->uobject) && (ibpd->uobject->context)) {
+ nesqp->user_mode = 1;
+ nes_ucontext = to_nesucontext(ibpd->uobject->context);
+ if (virt_wqs) {
+ err = 1;
+ list_for_each_entry(nespbl, &nes_ucontext->qp_reg_mem_list, list) {
+ if (nespbl->user_base == (unsigned long )req.user_wqe_buffers) {
+ list_del(&nespbl->list);
+ err = 0;
+ nes_debug(NES_DBG_QP, "Found PBL for virtual QP. nespbl=%p. user_base=0x%lx\n",
+ nespbl, nespbl->user_base);
+ break;
+ }
+ }
+ if (err) {
+ nes_debug(NES_DBG_QP, "Didn't Find PBL for virtual QP. address = %llx.\n",
+ (long long unsigned int)req.user_wqe_buffers);
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ kfree(nesqp->allocated_buffer);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ nes_ucontext = to_nesucontext(ibpd->uobject->context);
+ nesqp->mmap_sq_db_index =
+ find_next_zero_bit(nes_ucontext->allocated_wqs,
+ NES_MAX_USER_WQ_REGIONS, nes_ucontext->first_free_wq);
+ /* nes_debug(NES_DBG_QP, "find_first_zero_biton wqs returned %u\n",
+ nespd->mmap_db_index); */
+ if (nesqp->mmap_sq_db_index > NES_MAX_USER_WQ_REGIONS) {
+ nes_debug(NES_DBG_QP,
+ "db index > max user regions, failing create QP\n");
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ if (virt_wqs) {
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size, nespbl->pbl_vbase,
+ nespbl->pbl_pbase);
+ kfree(nespbl);
+ }
+ kfree(nesqp->allocated_buffer);
+ return ERR_PTR(-ENOMEM);
+ }
+ set_bit(nesqp->mmap_sq_db_index, nes_ucontext->allocated_wqs);
+ nes_ucontext->mmap_nesqp[nesqp->mmap_sq_db_index] = nesqp;
+ nes_ucontext->first_free_wq = nesqp->mmap_sq_db_index + 1;
+ } else {
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ kfree(nesqp->allocated_buffer);
+ return ERR_PTR(-EFAULT);
+ }
+ }
+ err = (!virt_wqs) ? nes_setup_mmap_qp(nesqp, nesvnic, sq_size, rq_size) :
+ nes_setup_virt_qp(nesqp, nespbl, nesvnic, sq_size, rq_size);
+ if (err) {
+ nes_debug(NES_DBG_QP,
+ "error geting qp mem code = %d\n", err);
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ kfree(nesqp->allocated_buffer);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nesqp->hwqp.sq_size = sq_size;
+ nesqp->hwqp.sq_encoded_size = sq_encoded_size;
+ nesqp->hwqp.sq_head = 1;
+ nesqp->hwqp.rq_size = rq_size;
+ nesqp->hwqp.rq_encoded_size = rq_encoded_size;
+ /* nes_debug(NES_DBG_QP, "nesqp->nesqp_context_pbase = %p\n",
+ (void *)nesqp->nesqp_context_pbase);
+ */
+ nesqp->hwqp.qp_id = qp_num;
+ nesqp->ibqp.qp_num = nesqp->hwqp.qp_id;
+ nesqp->nespd = nespd;
+
+ nescq = to_nescq(init_attr->send_cq);
+ nesqp->nesscq = nescq;
+ nescq = to_nescq(init_attr->recv_cq);
+ nesqp->nesrcq = nescq;
+
+ nesqp->nesqp_context->misc |= cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) <<
+ NES_QPCONTEXT_MISC_PCI_FCN_SHIFT);
+ nesqp->nesqp_context->misc |= cpu_to_le32((u32)nesqp->hwqp.rq_encoded_size <<
+ NES_QPCONTEXT_MISC_RQ_SIZE_SHIFT);
+ nesqp->nesqp_context->misc |= cpu_to_le32((u32)nesqp->hwqp.sq_encoded_size <<
+ NES_QPCONTEXT_MISC_SQ_SIZE_SHIFT);
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_PRIV_EN);
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_FAST_REGISTER_EN);
+ nesqp->nesqp_context->cqs = cpu_to_le32(nesqp->nesscq->hw_cq.cq_number +
+ ((u32)nesqp->nesrcq->hw_cq.cq_number << 16));
+ u64temp = (u64)nesqp->hwqp.sq_pbase;
+ nesqp->nesqp_context->sq_addr_low = cpu_to_le32((u32)u64temp);
+ nesqp->nesqp_context->sq_addr_high = cpu_to_le32((u32)(u64temp >> 32));
+
+
+ if (!virt_wqs) {
+ u64temp = (u64)nesqp->hwqp.sq_pbase;
+ nesqp->nesqp_context->sq_addr_low = cpu_to_le32((u32)u64temp);
+ nesqp->nesqp_context->sq_addr_high = cpu_to_le32((u32)(u64temp >> 32));
+ u64temp = (u64)nesqp->hwqp.rq_pbase;
+ nesqp->nesqp_context->rq_addr_low = cpu_to_le32((u32)u64temp);
+ nesqp->nesqp_context->rq_addr_high = cpu_to_le32((u32)(u64temp >> 32));
+ } else {
+ u64temp = (u64)nesqp->pbl_pbase;
+ nesqp->nesqp_context->rq_addr_low = cpu_to_le32((u32)u64temp);
+ nesqp->nesqp_context->rq_addr_high = cpu_to_le32((u32)(u64temp >> 32));
+ }
+
+ /* nes_debug(NES_DBG_QP, "next_qp_nic_index=%u, using nic_index=%d\n",
+ nesvnic->next_qp_nic_index,
+ nesvnic->qp_nic_index[nesvnic->next_qp_nic_index]); */
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ nesqp->nesqp_context->misc2 |= cpu_to_le32(
+ (u32)nesvnic->qp_nic_index[nesvnic->next_qp_nic_index] <<
+ NES_QPCONTEXT_MISC2_NIC_INDEX_SHIFT);
+ nesvnic->next_qp_nic_index++;
+ if ((nesvnic->next_qp_nic_index > 3) ||
+ (nesvnic->qp_nic_index[nesvnic->next_qp_nic_index] == 0xf)) {
+ nesvnic->next_qp_nic_index = 0;
+ }
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+
+ nesqp->nesqp_context->pd_index_wscale |= cpu_to_le32((u32)nesqp->nespd->pd_id << 16);
+ u64temp = (u64)nesqp->hwqp.q2_pbase;
+ nesqp->nesqp_context->q2_addr_low = cpu_to_le32((u32)u64temp);
+ nesqp->nesqp_context->q2_addr_high = cpu_to_le32((u32)(u64temp >> 32));
+ nesqp->nesqp_context->aeq_token_low = cpu_to_le32((u32)((unsigned long)(nesqp)));
+ nesqp->nesqp_context->aeq_token_high = cpu_to_le32((u32)(upper_32_bits((unsigned long)(nesqp))));
+ nesqp->nesqp_context->ird_ord_sizes = cpu_to_le32(NES_QPCONTEXT_ORDIRD_ALSMM |
+ ((((u32)nesadapter->max_irrq_wr) <<
+ NES_QPCONTEXT_ORDIRD_IRDSIZE_SHIFT) & NES_QPCONTEXT_ORDIRD_IRDSIZE_MASK));
+ if (disable_mpa_crc) {
+ nes_debug(NES_DBG_QP, "Disabling MPA crc checking due to module option.\n");
+ nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(NES_QPCONTEXT_ORDIRD_RNMC);
+ }
+
+
+ /* Create the QP */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_QP, "Failed to get a cqp_request\n");
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ nes_free_qp_mem(nesdev, nesqp,virt_wqs);
+ kfree(nesqp->allocated_buffer);
+ return ERR_PTR(-ENOMEM);
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ if (!virt_wqs) {
+ opcode = NES_CQP_CREATE_QP | NES_CQP_QP_TYPE_IWARP |
+ NES_CQP_QP_IWARP_STATE_IDLE;
+ } else {
+ opcode = NES_CQP_CREATE_QP | NES_CQP_QP_TYPE_IWARP | NES_CQP_QP_VIRT_WQS |
+ NES_CQP_QP_IWARP_STATE_IDLE;
+ }
+ opcode |= NES_CQP_QP_CQS_VALID;
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+
+ u64temp = (u64)nesqp->nesqp_context_pbase;
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, u64temp);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ nes_debug(NES_DBG_QP, "Waiting for create iWARP QP%u to complete.\n",
+ nesqp->hwqp.qp_id);
+ ret = wait_event_timeout(cqp_request->waitq,
+ (cqp_request->request_done != 0), NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_QP, "Create iwarp QP%u completed, wait_event_timeout ret=%u,"
+ " nesdev->cqp_head = %u, nesdev->cqp.sq_tail = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ nesqp->hwqp.qp_id, ret, nesdev->cqp.sq_head, nesdev->cqp.sq_tail,
+ cqp_request->major_code, cqp_request->minor_code);
+ if ((!ret) || (cqp_request->major_code)) {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ nes_free_qp_mem(nesdev, nesqp,virt_wqs);
+ kfree(nesqp->allocated_buffer);
+ if (!ret) {
+ return ERR_PTR(-ETIME);
+ } else {
+ return ERR_PTR(-EIO);
+ }
+ } else {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+
+ if (ibpd->uobject) {
+ uresp.mmap_sq_db_index = nesqp->mmap_sq_db_index;
+ uresp.actual_sq_size = sq_size;
+ uresp.actual_rq_size = rq_size;
+ uresp.qp_id = nesqp->hwqp.qp_id;
+ uresp.nes_drv_opt = nes_drv_opt;
+ if (ib_copy_to_udata(udata, &uresp, sizeof uresp)) {
+ nes_free_resource(nesadapter, nesadapter->allocated_qps, qp_num);
+ nes_free_qp_mem(nesdev, nesqp,virt_wqs);
+ kfree(nesqp->allocated_buffer);
+ return ERR_PTR(-EFAULT);
+ }
+ }
+
+ nes_debug(NES_DBG_QP, "QP%u structure located @%p.Size = %u.\n",
+ nesqp->hwqp.qp_id, nesqp, (u32)sizeof(*nesqp));
+ spin_lock_init(&nesqp->lock);
+ init_waitqueue_head(&nesqp->state_waitq);
+ init_waitqueue_head(&nesqp->kick_waitq);
+ nes_add_ref(&nesqp->ibqp);
+ break;
+ default:
+ nes_debug(NES_DBG_QP, "Invalid QP type: %d\n", init_attr->qp_type);
+ return ERR_PTR(-EINVAL);
+ break;
+ }
+
+ /* update the QP table */
+ nesdev->nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = nesqp;
+ nes_debug(NES_DBG_QP, "netdev refcnt=%u\n",
+ atomic_read(&nesvnic->netdev->refcnt));
+
+ return &nesqp->ibqp;
+}
+
+
+/**
+ * nes_destroy_qp
+ */
+static int nes_destroy_qp(struct ib_qp *ibqp)
+{
+ struct nes_qp *nesqp = to_nesqp(ibqp);
+ /* struct nes_vnic *nesvnic = to_nesvnic(ibqp->device); */
+ struct nes_ucontext *nes_ucontext;
+ struct ib_qp_attr attr;
+ struct iw_cm_id *cm_id;
+ struct iw_cm_event cm_event;
+ int ret;
+
+ atomic_inc(&sw_qps_destroyed);
+ nesqp->destroyed = 1;
+
+ /* Blow away the connection if it exists. */
+ if (nesqp->ibqp_state >= IB_QPS_INIT && nesqp->ibqp_state <= IB_QPS_RTS) {
+ /* if (nesqp->ibqp_state == IB_QPS_RTS) { */
+ attr.qp_state = IB_QPS_ERR;
+ nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
+ }
+
+ if (((nesqp->ibqp_state == IB_QPS_INIT) ||
+ (nesqp->ibqp_state == IB_QPS_RTR)) && (nesqp->cm_id)) {
+ cm_id = nesqp->cm_id;
+ cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
+ cm_event.status = IW_CM_EVENT_STATUS_TIMEOUT;
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+
+ nes_debug(NES_DBG_QP, "Generating a CM Timeout Event for "
+ "QP%u. cm_id = %p, refcount = %u. \n",
+ nesqp->hwqp.qp_id, cm_id, atomic_read(&nesqp->refcount));
+
+ cm_id->rem_ref(cm_id);
+ ret = cm_id->event_handler(cm_id, &cm_event);
+ if (ret)
+ nes_debug(NES_DBG_QP, "OFA CM event_handler returned, ret=%d\n", ret);
+ }
+
+
+ if (nesqp->user_mode) {
+ if ((ibqp->uobject)&&(ibqp->uobject->context)) {
+ nes_ucontext = to_nesucontext(ibqp->uobject->context);
+ clear_bit(nesqp->mmap_sq_db_index, nes_ucontext->allocated_wqs);
+ nes_ucontext->mmap_nesqp[nesqp->mmap_sq_db_index] = NULL;
+ if (nes_ucontext->first_free_wq > nesqp->mmap_sq_db_index) {
+ nes_ucontext->first_free_wq = nesqp->mmap_sq_db_index;
+ }
+ }
+ if (nesqp->pbl_pbase)
+ kunmap(nesqp->page);
+ }
+
+ nes_rem_ref(&nesqp->ibqp);
+ return 0;
+}
+
+
+/**
+ * nes_create_cq
+ */
+static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
+ int comp_vector,
+ struct ib_ucontext *context, struct ib_udata *udata)
+{
+ u64 u64temp;
+ struct nes_vnic *nesvnic = to_nesvnic(ibdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_cq *nescq;
+ struct nes_ucontext *nes_ucontext = NULL;
+ struct nes_cqp_request *cqp_request;
+ void *mem = NULL;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_pbl *nespbl = NULL;
+ struct nes_create_cq_req req;
+ struct nes_create_cq_resp resp;
+ u32 cq_num = 0;
+ u32 opcode = 0;
+ u32 pbl_entries = 1;
+ int err;
+ unsigned long flags;
+ int ret;
+
+ err = nes_alloc_resource(nesadapter, nesadapter->allocated_cqs,
+ nesadapter->max_cq, &cq_num, &nesadapter->next_cq);
+ if (err) {
+ return ERR_PTR(err);
+ }
+
+ nescq = kzalloc(sizeof(struct nes_cq), GFP_KERNEL);
+ if (!nescq) {
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ nes_debug(NES_DBG_CQ, "Unable to allocate nes_cq struct\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nescq->hw_cq.cq_size = max(entries + 1, 5);
+ nescq->hw_cq.cq_number = cq_num;
+ nescq->ibcq.cqe = nescq->hw_cq.cq_size - 1;
+
+
+ if (context) {
+ nes_ucontext = to_nesucontext(context);
+ if (ib_copy_from_udata(&req, udata, sizeof (struct nes_create_cq_req))) {
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-EFAULT);
+ }
+ nesvnic->mcrq_ucontext = nes_ucontext;
+ nes_ucontext->mcrqf = req.mcrqf;
+ if (nes_ucontext->mcrqf) {
+ if (nes_ucontext->mcrqf & 0x80000000)
+ nescq->hw_cq.cq_number = nesvnic->nic.qp_id + 12 + (nes_ucontext->mcrqf & 0xf) - 1;
+ else if (nes_ucontext->mcrqf & 0x40000000)
+ nescq->hw_cq.cq_number = nes_ucontext->mcrqf & 0xffff;
+ else
+ nescq->hw_cq.cq_number = nesvnic->mcrq_qp_id + nes_ucontext->mcrqf-1;
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ }
+ nes_debug(NES_DBG_CQ, "CQ Virtual Address = %08lX, size = %u.\n",
+ (unsigned long)req.user_cq_buffer, entries);
+ list_for_each_entry(nespbl, &nes_ucontext->cq_reg_mem_list, list) {
+ if (nespbl->user_base == (unsigned long )req.user_cq_buffer) {
+ list_del(&nespbl->list);
+ err = 0;
+ nes_debug(NES_DBG_CQ, "Found PBL for virtual CQ. nespbl=%p.\n",
+ nespbl);
+ break;
+ }
+ }
+ if (err) {
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(err);
+ }
+
+ pbl_entries = nespbl->pbl_size >> 3;
+ nescq->cq_mem_size = 0;
+ } else {
+ nescq->cq_mem_size = nescq->hw_cq.cq_size * sizeof(struct nes_hw_cqe);
+ nes_debug(NES_DBG_CQ, "Attempting to allocate pci memory (%u entries, %u bytes) for CQ%u.\n",
+ entries, nescq->cq_mem_size, nescq->hw_cq.cq_number);
+
+ /* allocate the physical buffer space */
+ mem = pci_alloc_consistent(nesdev->pcidev, nescq->cq_mem_size,
+ &nescq->hw_cq.cq_pbase);
+ if (!mem) {
+ printk(KERN_ERR PFX "Unable to allocate pci memory for cq\n");
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memset(mem, 0, nescq->cq_mem_size);
+ nescq->hw_cq.cq_vbase = mem;
+ nescq->hw_cq.cq_head = 0;
+ nes_debug(NES_DBG_CQ, "CQ%u virtual address @ %p, phys = 0x%08X\n",
+ nescq->hw_cq.cq_number, nescq->hw_cq.cq_vbase,
+ (u32)nescq->hw_cq.cq_pbase);
+ }
+
+ nescq->hw_cq.ce_handler = nes_iwarp_ce_handler;
+ spin_lock_init(&nescq->lock);
+
+ /* send CreateCQ request to CQP */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_CQ, "Failed to get a cqp_request.\n");
+ if (!context)
+ pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
+ nescq->hw_cq.cq_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-ENOMEM);
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ opcode = NES_CQP_CREATE_CQ | NES_CQP_CQ_CEQ_VALID |
+ NES_CQP_CQ_CHK_OVERFLOW |
+ NES_CQP_CQ_CEQE_MASK | ((u32)nescq->hw_cq.cq_size << 16);
+
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+
+ if (pbl_entries != 1) {
+ if (pbl_entries > 32) {
+ /* use 4k pbl */
+ nes_debug(NES_DBG_CQ, "pbl_entries=%u, use a 4k PBL\n", pbl_entries);
+ if (nesadapter->free_4kpbl == 0) {
+ if (cqp_request->dynamic) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kfree(cqp_request);
+ } else {
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ }
+ if (!context)
+ pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
+ nescq->hw_cq.cq_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-ENOMEM);
+ } else {
+ opcode |= (NES_CQP_CQ_VIRT | NES_CQP_CQ_4KB_CHUNK);
+ nescq->virtual_cq = 2;
+ nesadapter->free_4kpbl--;
+ }
+ } else {
+ /* use 256 byte pbl */
+ nes_debug(NES_DBG_CQ, "pbl_entries=%u, use a 256 byte PBL\n", pbl_entries);
+ if (nesadapter->free_256pbl == 0) {
+ if (cqp_request->dynamic) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kfree(cqp_request);
+ } else {
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ }
+ if (!context)
+ pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
+ nescq->hw_cq.cq_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-ENOMEM);
+ } else {
+ opcode |= NES_CQP_CQ_VIRT;
+ nescq->virtual_cq = 1;
+ nesadapter->free_256pbl--;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ (nescq->hw_cq.cq_number | ((u32)nesdev->ceq_index << 16)));
+
+ if (context) {
+ if (pbl_entries != 1)
+ u64temp = (u64)nespbl->pbl_pbase;
+ else
+ u64temp = le64_to_cpu(nespbl->pbl_vbase[0]);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX,
+ nes_ucontext->mmap_db_index[0]);
+ } else {
+ u64temp = (u64)nescq->hw_cq.cq_pbase;
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_DOORBELL_INDEX_HIGH_IDX] = 0;
+ }
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_CQ_WQE_PBL_LOW_IDX, u64temp);
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] = 0;
+ u64temp = (u64)(unsigned long)&nescq->hw_cq;
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_LOW_IDX] =
+ cpu_to_le32((u32)(u64temp >> 1));
+ cqp_wqe->wqe_words[NES_CQP_CQ_WQE_CQ_CONTEXT_HIGH_IDX] =
+ cpu_to_le32(((u32)((u64temp) >> 33)) & 0x7FFFFFFF);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ nes_debug(NES_DBG_CQ, "Waiting for create iWARP CQ%u to complete.\n",
+ nescq->hw_cq.cq_number);
+ ret = wait_event_timeout(cqp_request->waitq, (0 != cqp_request->request_done),
+ NES_EVENT_TIMEOUT * 2);
+ nes_debug(NES_DBG_CQ, "Create iWARP CQ%u completed, wait_event_timeout ret = %d.\n",
+ nescq->hw_cq.cq_number, ret);
+ if ((!ret) || (cqp_request->major_code)) {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ nes_debug(NES_DBG_CQ, "iWARP CQ%u create timeout expired, major code = 0x%04X,"
+ " minor code = 0x%04X\n",
+ nescq->hw_cq.cq_number, cqp_request->major_code, cqp_request->minor_code);
+ if (!context)
+ pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
+ nescq->hw_cq.cq_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-EIO);
+ } else {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+
+ if (context) {
+ /* free the nespbl */
+ pci_free_consistent(nesdev->pcidev, nespbl->pbl_size, nespbl->pbl_vbase,
+ nespbl->pbl_pbase);
+ kfree(nespbl);
+ resp.cq_id = nescq->hw_cq.cq_number;
+ resp.cq_size = nescq->hw_cq.cq_size;
+ resp.mmap_db_index = 0;
+ if (ib_copy_to_udata(udata, &resp, sizeof resp)) {
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, cq_num);
+ kfree(nescq);
+ return ERR_PTR(-EFAULT);
+ }
+ }
+
+ return &nescq->ibcq;
+}
+
+
+/**
+ * nes_destroy_cq
+ */
+static int nes_destroy_cq(struct ib_cq *ib_cq)
+{
+ struct nes_cq *nescq;
+ struct nes_device *nesdev;
+ struct nes_vnic *nesvnic;
+ struct nes_adapter *nesadapter;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ unsigned long flags;
+ u32 opcode = 0;
+ int ret;
+
+ if (ib_cq == NULL)
+ return 0;
+
+ nescq = to_nescq(ib_cq);
+ nesvnic = to_nesvnic(ib_cq->device);
+ nesdev = nesvnic->nesdev;
+ nesadapter = nesdev->nesadapter;
+
+ nes_debug(NES_DBG_CQ, "Destroy CQ%u\n", nescq->hw_cq.cq_number);
+
+ /* Send DestroyCQ request to CQP */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_CQ, "Failed to get a cqp_request.\n");
+ return -ENOMEM;
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+ opcode = NES_CQP_DESTROY_CQ | (nescq->hw_cq.cq_size << 16);
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ if (nescq->virtual_cq == 1) {
+ nesadapter->free_256pbl++;
+ if (nesadapter->free_256pbl > nesadapter->max_256pbl) {
+ printk(KERN_ERR PFX "%s: free 256B PBLs(%u) has exceeded the max(%u)\n",
+ __FUNCTION__, nesadapter->free_256pbl, nesadapter->max_256pbl);
+ }
+ } else if (nescq->virtual_cq == 2) {
+ nesadapter->free_4kpbl++;
+ if (nesadapter->free_4kpbl > nesadapter->max_4kpbl) {
+ printk(KERN_ERR PFX "%s: free 4K PBLs(%u) has exceeded the max(%u)\n",
+ __FUNCTION__, nesadapter->free_4kpbl, nesadapter->max_4kpbl);
+ }
+ opcode |= NES_CQP_CQ_4KB_CHUNK;
+ }
+
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX,
+ (nescq->hw_cq.cq_number | ((u32)PCI_FUNC(nesdev->pcidev->devfn) << 16)));
+ nes_free_resource(nesadapter, nesadapter->allocated_cqs, nescq->hw_cq.cq_number);
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ nes_debug(NES_DBG_CQ, "Waiting for destroy iWARP CQ%u to complete.\n",
+ nescq->hw_cq.cq_number);
+ ret = wait_event_timeout(cqp_request->waitq, (0 != cqp_request->request_done),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_CQ, "Destroy iWARP CQ%u completed, wait_event_timeout ret = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ nescq->hw_cq.cq_number, ret, cqp_request->major_code,
+ cqp_request->minor_code);
+ if ((!ret) || (cqp_request->major_code)) {
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ if (!ret) {
+ nes_debug(NES_DBG_CQ, "iWARP CQ%u destroy timeout expired\n",
+ nescq->hw_cq.cq_number);
+ ret = -ETIME;
+ } else {
+ nes_debug(NES_DBG_CQ, "iWARP CQ%u destroy failed\n",
+ nescq->hw_cq.cq_number);
+ ret = -EIO;
+ }
+ } else {
+ ret = 0;
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ }
+
+ if (nescq->cq_mem_size)
+ pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size,
+ (void *)nescq->hw_cq.cq_vbase, nescq->hw_cq.cq_pbase);
+ kfree(nescq);
+
+ return ret;
+}
+
+
+/**
+ * nes_reg_mr
+ */
+static int nes_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd,
+ u32 stag, u64 region_length, struct nes_root_vpbl *root_vpbl,
+ dma_addr_t single_buffer, u16 pbl_count, u16 residual_page_count,
+ int acc, u64 *iova_start)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ unsigned long flags;
+ int ret;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ /* int count; */
+ u32 opcode = 0;
+ u16 major_code;
+
+ /* Register the region with the adapter */
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_MR, "Failed to get a cqp_request.\n");
+ return -ENOMEM;
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ /* track PBL resources */
+ if (pbl_count != 0) {
+ if (pbl_count > 1) {
+ /* Two level PBL */
+ if ((pbl_count+1) > nesadapter->free_4kpbl) {
+ nes_debug(NES_DBG_MR, "Out of 4KB Pbls for two level request.\n");
+ if (cqp_request->dynamic) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kfree(cqp_request);
+ } else {
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ }
+ return -ENOMEM;
+ } else {
+ nesadapter->free_4kpbl -= pbl_count+1;
+ }
+ } else if (residual_page_count > 32) {
+ if (pbl_count > nesadapter->free_4kpbl) {
+ nes_debug(NES_DBG_MR, "Out of 4KB Pbls.\n");
+ if (cqp_request->dynamic) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kfree(cqp_request);
+ } else {
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ }
+ return -ENOMEM;
+ } else {
+ nesadapter->free_4kpbl -= pbl_count;
+ }
+ } else {
+ if (pbl_count > nesadapter->free_256pbl) {
+ nes_debug(NES_DBG_MR, "Out of 256B Pbls.\n");
+ if (cqp_request->dynamic) {
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ kfree(cqp_request);
+ } else {
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ }
+ return -ENOMEM;
+ } else {
+ nesadapter->free_256pbl -= pbl_count;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+
+ opcode = NES_CQP_REGISTER_STAG | NES_CQP_STAG_RIGHTS_LOCAL_READ |
+ NES_CQP_STAG_VA_TO | NES_CQP_STAG_MR;
+ if (acc & IB_ACCESS_LOCAL_WRITE)
+ opcode |= NES_CQP_STAG_RIGHTS_LOCAL_WRITE;
+ if (acc & IB_ACCESS_REMOTE_WRITE)
+ opcode |= NES_CQP_STAG_RIGHTS_REMOTE_WRITE | NES_CQP_STAG_REM_ACC_EN;
+ if (acc & IB_ACCESS_REMOTE_READ)
+ opcode |= NES_CQP_STAG_RIGHTS_REMOTE_READ | NES_CQP_STAG_REM_ACC_EN;
+ if (acc & IB_ACCESS_MW_BIND)
+ opcode |= NES_CQP_STAG_RIGHTS_WINDOW_BIND | NES_CQP_STAG_REM_ACC_EN;
+
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX, opcode);
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_VA_LOW_IDX, *iova_start);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_LEN_LOW_IDX, region_length);
+
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_LEN_HIGH_PD_IDX] =
+ cpu_to_le32((u32)(region_length >> 8) & 0xff000000);
+ cqp_wqe->wqe_words[NES_CQP_STAG_WQE_LEN_HIGH_PD_IDX] |=
+ cpu_to_le32(nespd->pd_id & 0x00007fff);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_STAG_IDX, stag);
+
+ if (pbl_count == 0) {
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_PA_LOW_IDX, single_buffer);
+ } else {
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_PA_LOW_IDX, root_vpbl->pbl_pbase);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_PBL_BLK_COUNT_IDX, pbl_count);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_PBL_LEN_IDX,
+ (((pbl_count - 1) * 4096) + (residual_page_count*8)));
+
+ if ((pbl_count > 1) || (residual_page_count > 32))
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] |= cpu_to_le32(NES_CQP_STAG_PBL_BLK_SIZE);
+ }
+ barrier();
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ ret = wait_event_timeout(cqp_request->waitq, (0 != cqp_request->request_done),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_MR, "Register STag 0x%08X completed, wait_event_timeout ret = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ stag, ret, cqp_request->major_code, cqp_request->minor_code);
+ major_code = cqp_request->major_code;
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ if (!ret)
+ return -ETIME;
+ else if (major_code)
+ return -EIO;
+ else
+ return 0;
+
+ return 0;
+}
+
+
+/**
+ * nes_reg_phys_mr
+ */
+static struct ib_mr *nes_reg_phys_mr(struct ib_pd *ib_pd,
+ struct ib_phys_buf *buffer_list, int num_phys_buf, int acc,
+ u64 * iova_start)
+{
+ u64 region_length;
+ struct nes_pd *nespd = to_nespd(ib_pd);
+ struct nes_vnic *nesvnic = to_nesvnic(ib_pd->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_mr *nesmr;
+ struct ib_mr *ibmr;
+ struct nes_vpbl vpbl;
+ struct nes_root_vpbl root_vpbl;
+ u32 stag;
+ u32 i;
+ u32 stag_index = 0;
+ u32 next_stag_index = 0;
+ u32 driver_key = 0;
+ u32 root_pbl_index = 0;
+ u32 cur_pbl_index = 0;
+ int err = 0, pbl_depth = 0;
+ int ret = 0;
+ u16 pbl_count = 0;
+ u8 single_page = 1;
+ u8 stag_key = 0;
+
+ pbl_depth = 0;
+ region_length = 0;
+ vpbl.pbl_vbase = NULL;
+ root_vpbl.pbl_vbase = NULL;
+ root_vpbl.pbl_pbase = 0;
+
+ get_random_bytes(&next_stag_index, sizeof(next_stag_index));
+ stag_key = (u8)next_stag_index;
+
+ driver_key = 0;
+
+ next_stag_index >>= 8;
+ next_stag_index %= nesadapter->max_mr;
+ if (num_phys_buf > (1024*512)) {
+ return ERR_PTR(-E2BIG);
+ }
+
+ err = nes_alloc_resource(nesadapter, nesadapter->allocated_mrs, nesadapter->max_mr,
+ &stag_index, &next_stag_index);
+ if (err) {
+ return ERR_PTR(err);
+ }
+
+ nesmr = kzalloc(sizeof(*nesmr), GFP_KERNEL);
+ if (!nesmr) {
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ for (i = 0; i < num_phys_buf; i++) {
+
+ if ((i & 0x01FF) == 0) {
+ if (root_pbl_index == 1) {
+ /* Allocate the root PBL */
+ root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 8192,
+ &root_vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n",
+ root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase);
+ if (!root_vpbl.pbl_vbase) {
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ kfree(nesmr);
+ return ERR_PTR(-ENOMEM);
+ }
+ root_vpbl.leaf_vpbl = kzalloc(sizeof(*root_vpbl.leaf_vpbl)*1024, GFP_KERNEL);
+ if (!root_vpbl.leaf_vpbl) {
+ pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
+ root_vpbl.pbl_pbase);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ kfree(nesmr);
+ return ERR_PTR(-ENOMEM);
+ }
+ root_vpbl.pbl_vbase[0].pa_low = cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[0].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
+ root_vpbl.leaf_vpbl[0] = vpbl;
+ }
+ /* Allocate a 4K buffer for the PBL */
+ vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
+ &vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%016lX\n",
+ vpbl.pbl_vbase, (unsigned long)vpbl.pbl_pbase);
+ if (!vpbl.pbl_vbase) {
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ ibmr = ERR_PTR(-ENOMEM);
+ kfree(nesmr);
+ goto reg_phys_err;
+ }
+ /* Fill in the root table */
+ if (1 <= root_pbl_index) {
+ root_vpbl.pbl_vbase[root_pbl_index].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[root_pbl_index].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
+ root_vpbl.leaf_vpbl[root_pbl_index] = vpbl;
+ }
+ root_pbl_index++;
+ cur_pbl_index = 0;
+ }
+ if (buffer_list[i].addr & ~PAGE_MASK) {
+ /* TODO: Unwind allocated buffers */
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n",
+ (unsigned int) buffer_list[i].addr);
+ ibmr = ERR_PTR(-EINVAL);
+ kfree(nesmr);
+ goto reg_phys_err;
+ }
+
+ if (!buffer_list[i].size) {
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ nes_debug(NES_DBG_MR, "Invalid Buffer Size\n");
+ ibmr = ERR_PTR(-EINVAL);
+ kfree(nesmr);
+ goto reg_phys_err;
+ }
+
+ region_length += buffer_list[i].size;
+ if ((i != 0) && (single_page)) {
+ if ((buffer_list[i-1].addr+PAGE_SIZE) != buffer_list[i].addr)
+ single_page = 0;
+ }
+ vpbl.pbl_vbase[cur_pbl_index].pa_low = cpu_to_le32((u32)buffer_list[i].addr);
+ vpbl.pbl_vbase[cur_pbl_index++].pa_high =
+ cpu_to_le32((u32)((((u64)buffer_list[i].addr) >> 32)));
+ }
+
+ stag = stag_index << 8;
+ stag |= driver_key;
+ stag += (u32)stag_key;
+
+ nes_debug(NES_DBG_MR, "Registering STag 0x%08X, VA = 0x%016lX,"
+ " length = 0x%016lX, index = 0x%08X\n",
+ stag, (unsigned long)*iova_start, (unsigned long)region_length, stag_index);
+
+ region_length -= (*iova_start)&PAGE_MASK;
+
+ /* Make the leaf PBL the root if only one PBL */
+ if (root_pbl_index == 1) {
+ root_vpbl.pbl_pbase = vpbl.pbl_pbase;
+ }
+
+ if (single_page) {
+ pbl_count = 0;
+ } else {
+ pbl_count = root_pbl_index;
+ }
+ ret = nes_reg_mr(nesdev, nespd, stag, region_length, &root_vpbl,
+ buffer_list[0].addr, pbl_count, (u16)cur_pbl_index, acc, iova_start);
+
+ if (ret == 0) {
+ nesmr->ibmr.rkey = stag;
+ nesmr->ibmr.lkey = stag;
+ nesmr->mode = IWNES_MEMREG_TYPE_MEM;
+ ibmr = &nesmr->ibmr;
+ nesmr->pbl_4k = ((pbl_count > 1) || (cur_pbl_index > 32)) ? 1 : 0;
+ nesmr->pbls_used = pbl_count;
+ if (pbl_count > 1) {
+ nesmr->pbls_used++;
+ }
+ } else {
+ kfree(nesmr);
+ ibmr = ERR_PTR(-ENOMEM);
+ }
+
+ reg_phys_err:
+ /* free the resources */
+ if (root_pbl_index == 1) {
+ /* single PBL case */
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase, vpbl.pbl_pbase);
+ } else {
+ for (i=0; i<root_pbl_index; i++) {
+ pci_free_consistent(nesdev->pcidev, 4096, root_vpbl.leaf_vpbl[i].pbl_vbase,
+ root_vpbl.leaf_vpbl[i].pbl_pbase);
+ }
+ kfree(root_vpbl.leaf_vpbl);
+ pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
+ root_vpbl.pbl_pbase);
+ }
+
+ return ibmr;
+}
+
+
+/**
+ * nes_get_dma_mr
+ */
+static struct ib_mr *nes_get_dma_mr(struct ib_pd *pd, int acc)
+{
+ struct ib_phys_buf bl;
+ u64 kva = 0;
+
+ nes_debug(NES_DBG_MR, "\n");
+
+ bl.size = (u64)0xffffffffffULL;
+ bl.addr = 0;
+ return nes_reg_phys_mr(pd, &bl, 1, acc, &kva);
+}
+
+
+/**
+ * nes_reg_user_mr
+ */
+static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
+ u64 virt, int acc, struct ib_udata *udata)
+{
+ u64 iova_start;
+ __le64 *pbl;
+ u64 region_length;
+ dma_addr_t last_dma_addr = 0;
+ dma_addr_t first_dma_addr = 0;
+ struct nes_pd *nespd = to_nespd(pd);
+ struct nes_vnic *nesvnic = to_nesvnic(pd->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct ib_mr *ibmr = ERR_PTR(-EINVAL);
+ struct ib_umem_chunk *chunk;
+ struct nes_ucontext *nes_ucontext;
+ struct nes_pbl *nespbl;
+ struct nes_mr *nesmr;
+ struct ib_umem *region;
+ struct nes_mem_reg_req req;
+ struct nes_vpbl vpbl;
+ struct nes_root_vpbl root_vpbl;
+ int nmap_index, page_index;
+ int page_count = 0;
+ int err, pbl_depth = 0;
+ int chunk_pages;
+ int ret;
+ u32 stag;
+ u32 stag_index = 0;
+ u32 next_stag_index;
+ u32 driver_key;
+ u32 root_pbl_index = 0;
+ u32 cur_pbl_index = 0;
+ u32 skip_pages;
+ u16 pbl_count;
+ u8 single_page = 1;
+ u8 stag_key;
+
+ region = ib_umem_get(pd->uobject->context, start, length, acc);
+ if (IS_ERR(region)) {
+ return (struct ib_mr *)region;
+ }
+
+ nes_debug(NES_DBG_MR, "User base = 0x%lX, Virt base = 0x%lX, length = %u,"
+ " offset = %u, page size = %u.\n",
+ (unsigned long int)start, (unsigned long int)virt, (u32)length,
+ region->offset, region->page_size);
+
+ skip_pages = ((u32)region->offset) >> 12;
+
+ if (ib_copy_from_udata(&req, udata, sizeof(req)))
+ return ERR_PTR(-EFAULT);
+ nes_debug(NES_DBG_MR, "Memory Registration type = %08X.\n", req.reg_type);
+
+ switch (req.reg_type) {
+ case IWNES_MEMREG_TYPE_MEM:
+ pbl_depth = 0;
+ region_length = 0;
+ vpbl.pbl_vbase = NULL;
+ root_vpbl.pbl_vbase = NULL;
+ root_vpbl.pbl_pbase = 0;
+
+ get_random_bytes(&next_stag_index, sizeof(next_stag_index));
+ stag_key = (u8)next_stag_index;
+
+ driver_key = next_stag_index & 0x70000000;
+
+ next_stag_index >>= 8;
+ next_stag_index %= nesadapter->max_mr;
+
+ err = nes_alloc_resource(nesadapter, nesadapter->allocated_mrs,
+ nesadapter->max_mr, &stag_index, &next_stag_index);
+ if (err) {
+ ib_umem_release(region);
+ return ERR_PTR(err);
+ }
+
+ nesmr = kzalloc(sizeof(*nesmr), GFP_KERNEL);
+ if (!nesmr) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ return ERR_PTR(-ENOMEM);
+ }
+ nesmr->region = region;
+
+ list_for_each_entry(chunk, ®ion->chunk_list, list) {
+ nes_debug(NES_DBG_MR, "Chunk: nents = %u, nmap = %u .\n",
+ chunk->nents, chunk->nmap);
+ for (nmap_index = 0; nmap_index < chunk->nmap; ++nmap_index) {
+ if (sg_dma_address(&chunk->page_list[nmap_index]) & ~PAGE_MASK) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ nes_debug(NES_DBG_MR, "Unaligned Memory Buffer: 0x%x\n",
+ (unsigned int) sg_dma_address(&chunk->page_list[nmap_index]));
+ ibmr = ERR_PTR(-EINVAL);
+ kfree(nesmr);
+ goto reg_user_mr_err;
+ }
+
+ if (!sg_dma_len(&chunk->page_list[nmap_index])) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
+ nes_debug(NES_DBG_MR, "Invalid Buffer Size\n");
+ ibmr = ERR_PTR(-EINVAL);
+ kfree(nesmr);
+ goto reg_user_mr_err;
+ }
+
+ region_length += sg_dma_len(&chunk->page_list[nmap_index]);
+ chunk_pages = sg_dma_len(&chunk->page_list[nmap_index]) >> 12;
+ region_length -= skip_pages << 12;
+ for (page_index=skip_pages; page_index < chunk_pages; page_index++) {
+ skip_pages = 0;
+ if ((page_count!=0)&&(page_count<<12)-(region->offset&(4096-1))>=region->length)
+ goto enough_pages;
+ if ((page_count&0x01FF) == 0) {
+ if (page_count>(1024*512)) {
+ ib_umem_release(region);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter,
+ nesadapter->allocated_mrs, stag_index);
+ kfree(nesmr);
+ ibmr = ERR_PTR(-E2BIG);
+ goto reg_user_mr_err;
+ }
+ if (root_pbl_index == 1) {
+ root_vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev,
+ 8192, &root_vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating root PBL, va = %p, pa = 0x%08X\n",
+ root_vpbl.pbl_vbase, (unsigned int)root_vpbl.pbl_pbase);
+ if (!root_vpbl.pbl_vbase) {
+ ib_umem_release(region);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
+ kfree(nesmr);
+ ibmr = ERR_PTR(-ENOMEM);
+ goto reg_user_mr_err;
+ }
+ root_vpbl.leaf_vpbl = kzalloc(sizeof(*root_vpbl.leaf_vpbl)*1024,
+ GFP_KERNEL);
+ if (!root_vpbl.leaf_vpbl) {
+ ib_umem_release(region);
+ pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
+ root_vpbl.pbl_pbase);
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ stag_index);
+ kfree(nesmr);
+ ibmr = ERR_PTR(-ENOMEM);
+ goto reg_user_mr_err;
+ }
+ root_vpbl.pbl_vbase[0].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[0].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase) >> 32)));
+ root_vpbl.leaf_vpbl[0] = vpbl;
+ }
+ vpbl.pbl_vbase = pci_alloc_consistent(nesdev->pcidev, 4096,
+ &vpbl.pbl_pbase);
+ nes_debug(NES_DBG_MR, "Allocating leaf PBL, va = %p, pa = 0x%08X\n",
+ vpbl.pbl_vbase, (unsigned int)vpbl.pbl_pbase);
+ if (!vpbl.pbl_vbase) {
+ ib_umem_release(region);
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs, stag_index);
+ ibmr = ERR_PTR(-ENOMEM);
+ kfree(nesmr);
+ goto reg_user_mr_err;
+ }
+ if (1 <= root_pbl_index) {
+ root_vpbl.pbl_vbase[root_pbl_index].pa_low =
+ cpu_to_le32((u32)vpbl.pbl_pbase);
+ root_vpbl.pbl_vbase[root_pbl_index].pa_high =
+ cpu_to_le32((u32)((((u64)vpbl.pbl_pbase)>>32)));
+ root_vpbl.leaf_vpbl[root_pbl_index] = vpbl;
+ }
+ root_pbl_index++;
+ cur_pbl_index = 0;
+ }
+ if (single_page) {
+ if (page_count != 0) {
+ if ((last_dma_addr+4096) !=
+ (sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096)))
+ single_page = 0;
+ last_dma_addr = sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096);
+ } else {
+ first_dma_addr = sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096);
+ last_dma_addr = first_dma_addr;
+ }
+ }
+
+ vpbl.pbl_vbase[cur_pbl_index].pa_low =
+ cpu_to_le32((u32)(sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096)));
+ vpbl.pbl_vbase[cur_pbl_index].pa_high =
+ cpu_to_le32((u32)((((u64)(sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096))) >> 32)));
+ cur_pbl_index++;
+ page_count++;
+ }
+ }
+ }
+ enough_pages:
+ nes_debug(NES_DBG_MR, "calculating stag, stag_index=0x%08x, driver_key=0x%08x,"
+ " stag_key=0x%08x\n",
+ stag_index, driver_key, stag_key);
+ stag = stag_index << 8;
+ stag |= driver_key;
+ stag += (u32)stag_key;
+ if (stag == 0) {
+ stag = 1;
+ }
+
+ iova_start = virt;
+ /* Make the leaf PBL the root if only one PBL */
+ if (root_pbl_index == 1) {
+ root_vpbl.pbl_pbase = vpbl.pbl_pbase;
+ }
+
+ if (single_page) {
+ pbl_count = 0;
+ } else {
+ pbl_count = root_pbl_index;
+ first_dma_addr = 0;
+ }
+ nes_debug(NES_DBG_MR, "Registering STag 0x%08X, VA = 0x%08X, length = 0x%08X,"
+ " index = 0x%08X, region->length=0x%08llx, pbl_count = %u\n",
+ stag, (unsigned int)iova_start,
+ (unsigned int)region_length, stag_index,
+ (unsigned long long)region->length, pbl_count);
+ ret = nes_reg_mr( nesdev, nespd, stag, region->length, &root_vpbl,
+ first_dma_addr, pbl_count, (u16)cur_pbl_index, acc, &iova_start);
+
+ nes_debug(NES_DBG_MR, "ret=%d\n", ret);
+
+ if (ret == 0) {
+ nesmr->ibmr.rkey = stag;
+ nesmr->ibmr.lkey = stag;
+ nesmr->mode = IWNES_MEMREG_TYPE_MEM;
+ ibmr = &nesmr->ibmr;
+ nesmr->pbl_4k = ((pbl_count > 1) || (cur_pbl_index > 32)) ? 1 : 0;
+ nesmr->pbls_used = pbl_count;
+ if (pbl_count > 1) {
+ nesmr->pbls_used++;
+ }
+ } else {
+ ib_umem_release(region);
+ kfree(nesmr);
+ ibmr = ERR_PTR(-ENOMEM);
+ }
+
+ reg_user_mr_err:
+ /* free the resources */
+ if (root_pbl_index == 1) {
+ pci_free_consistent(nesdev->pcidev, 4096, vpbl.pbl_vbase,
+ vpbl.pbl_pbase);
+ } else {
+ for (page_index=0; page_index<root_pbl_index; page_index++) {
+ pci_free_consistent(nesdev->pcidev, 4096,
+ root_vpbl.leaf_vpbl[page_index].pbl_vbase,
+ root_vpbl.leaf_vpbl[page_index].pbl_pbase);
+ }
+ kfree(root_vpbl.leaf_vpbl);
+ pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase,
+ root_vpbl.pbl_pbase);
+ }
+
+ nes_debug(NES_DBG_MR, "Leaving, ibmr=%p", ibmr);
+
+ return ibmr;
+ break;
+ case IWNES_MEMREG_TYPE_QP:
+ case IWNES_MEMREG_TYPE_CQ:
+ nespbl = kzalloc(sizeof(*nespbl), GFP_KERNEL);
+ if (!nespbl) {
+ nes_debug(NES_DBG_MR, "Unable to allocate PBL\n");
+ ib_umem_release(region);
+ return ERR_PTR(-ENOMEM);
+ }
+ nesmr = kzalloc(sizeof(*nesmr), GFP_KERNEL);
+ if (!nesmr) {
+ ib_umem_release(region);
+ kfree(nespbl);
+ nes_debug(NES_DBG_MR, "Unable to allocate nesmr\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ nesmr->region = region;
+ nes_ucontext = to_nesucontext(pd->uobject->context);
+ pbl_depth = region->length >> 12;
+ pbl_depth += (region->length & (4096-1)) ? 1 : 0;
+ nespbl->pbl_size = pbl_depth*sizeof(u64);
+ if (req.reg_type == IWNES_MEMREG_TYPE_QP) {
+ nes_debug(NES_DBG_MR, "Attempting to allocate QP PBL memory");
+ } else {
+ nes_debug(NES_DBG_MR, "Attempting to allocate CP PBL memory");
+ }
+
+ nes_debug(NES_DBG_MR, " %u bytes, %u entries.\n",
+ nespbl->pbl_size, pbl_depth);
+ pbl = pci_alloc_consistent(nesdev->pcidev, nespbl->pbl_size,
+ &nespbl->pbl_pbase);
+ if (!pbl) {
+ ib_umem_release(region);
+ kfree(nesmr);
+ kfree(nespbl);
+ nes_debug(NES_DBG_MR, "Unable to allocate PBL memory\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nespbl->pbl_vbase = (u64 *)pbl;
+ nespbl->user_base = start;
+ nes_debug(NES_DBG_MR, "Allocated PBL memory, %u bytes, pbl_pbase=%p,"
+ " pbl_vbase=%p user_base=0x%lx\n",
+ nespbl->pbl_size, (void *)nespbl->pbl_pbase,
+ (void*)nespbl->pbl_vbase, nespbl->user_base);
+
+ list_for_each_entry(chunk, ®ion->chunk_list, list) {
+ for (nmap_index = 0; nmap_index < chunk->nmap; ++nmap_index) {
+ chunk_pages = sg_dma_len(&chunk->page_list[nmap_index]) >> 12;
+ chunk_pages += (sg_dma_len(&chunk->page_list[nmap_index]) & (4096-1)) ? 1 : 0;
+ nespbl->page = sg_page(&chunk->page_list[0]);
+ for (page_index=0; page_index<chunk_pages; page_index++) {
+ ((__le32 *)pbl)[0] = cpu_to_le32((u32)
+ (sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096)));
+ ((__le32 *)pbl)[1] = cpu_to_le32(((u64)
+ (sg_dma_address(&chunk->page_list[nmap_index])+
+ (page_index*4096)))>>32);
+ nes_debug(NES_DBG_MR, "pbl=%p, *pbl=0x%016llx, 0x%08x%08x\n", pbl,
+ (unsigned long long)*pbl,
+ le32_to_cpu(((__le32 *)pbl)[1]), le32_to_cpu(((__le32 *)pbl)[0]));
+ pbl++;
+ }
+ }
+ }
+ if (req.reg_type == IWNES_MEMREG_TYPE_QP) {
+ list_add_tail(&nespbl->list, &nes_ucontext->qp_reg_mem_list);
+ } else {
+ list_add_tail(&nespbl->list, &nes_ucontext->cq_reg_mem_list);
+ }
+ nesmr->ibmr.rkey = -1;
+ nesmr->ibmr.lkey = -1;
+ nesmr->mode = req.reg_type;
+ return &nesmr->ibmr;
+ break;
+ }
+
+ return ERR_PTR(-ENOSYS);
+}
+
+
+/**
+ * nes_dereg_mr
+ */
+static int nes_dereg_mr(struct ib_mr *ib_mr)
+{
+ struct nes_mr *nesmr = to_nesmr(ib_mr);
+ struct nes_vnic *nesvnic = to_nesvnic(ib_mr->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_cqp_request *cqp_request;
+ unsigned long flags;
+ int ret;
+ u16 major_code;
+ u16 minor_code;
+
+ if (nesmr->region) {
+ ib_umem_release(nesmr->region);
+ }
+ if (nesmr->mode != IWNES_MEMREG_TYPE_MEM) {
+ kfree(nesmr);
+ return 0;
+ }
+
+ /* Deallocate the region with the adapter */
+
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_MR, "Failed to get a cqp_request.\n");
+ return -ENOMEM;
+ }
+ cqp_request->waiting = 1;
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ spin_lock_irqsave(&nesadapter->pbl_lock, flags);
+ if (nesmr->pbls_used != 0) {
+ if (nesmr->pbl_4k) {
+ nesadapter->free_4kpbl += nesmr->pbls_used;
+ if (nesadapter->free_4kpbl > nesadapter->max_4kpbl) {
+ printk(KERN_ERR PFX "free 4KB PBLs(%u) has exceeded the max(%u)\n",
+ nesadapter->free_4kpbl, nesadapter->max_4kpbl);
+ }
+ } else {
+ nesadapter->free_256pbl += nesmr->pbls_used;
+ if (nesadapter->free_256pbl > nesadapter->max_256pbl) {
+ printk(KERN_ERR PFX "free 256B PBLs(%u) has exceeded the max(%u)\n",
+ nesadapter->free_256pbl, nesadapter->max_256pbl);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ NES_CQP_DEALLOCATE_STAG | NES_CQP_STAG_VA_TO |
+ NES_CQP_STAG_DEALLOC_PBLS | NES_CQP_STAG_MR);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_STAG_WQE_STAG_IDX, ib_mr->rkey);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ nes_debug(NES_DBG_MR, "Waiting for deallocate STag 0x%08X completed\n", ib_mr->rkey);
+ ret = wait_event_timeout(cqp_request->waitq, (cqp_request->request_done != 0),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_MR, "Deallocate STag 0x%08X completed, wait_event_timeout ret = %u,"
+ " CQP Major:Minor codes = 0x%04X:0x%04X\n",
+ ib_mr->rkey, ret, cqp_request->major_code, cqp_request->minor_code);
+
+ nes_free_resource(nesadapter, nesadapter->allocated_mrs,
+ (ib_mr->rkey & 0x0fffff00) >> 8);
+
+ kfree(nesmr);
+
+ major_code = cqp_request->major_code;
+ minor_code = cqp_request->minor_code;
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ if (!ret) {
+ nes_debug(NES_DBG_MR, "Timeout waiting to destroy STag,"
+ " ib_mr=%p, rkey = 0x%08X\n",
+ ib_mr, ib_mr->rkey);
+ return -ETIME;
+ } else if (major_code) {
+ nes_debug(NES_DBG_MR, "Error (0x%04X:0x%04X) while attempting"
+ " to destroy STag, ib_mr=%p, rkey = 0x%08X\n",
+ major_code, minor_code, ib_mr, ib_mr->rkey);
+ return -EIO;
+ } else
+ return 0;
+}
+
+
+/**
+ * show_rev
+ */
+static ssize_t show_rev(struct class_device *cdev, char *buf)
+{
+ struct nes_ib_device *nesibdev =
+ container_of(cdev, struct nes_ib_device, ibdev.class_dev);
+ struct nes_vnic *nesvnic = nesibdev->nesvnic;
+
+ nes_debug(NES_DBG_INIT, "\n");
+ return sprintf(buf, "%x\n", nesvnic->nesdev->nesadapter->hw_rev);
+}
+
+
+/**
+ * show_fw_ver
+ */
+static ssize_t show_fw_ver(struct class_device *cdev, char *buf)
+{
+ struct nes_ib_device *nesibdev =
+ container_of(cdev, struct nes_ib_device, ibdev.class_dev);
+ struct nes_vnic *nesvnic = nesibdev->nesvnic;
+
+ nes_debug(NES_DBG_INIT, "\n");
+ return sprintf(buf, "%x.%x.%x\n",
+ (int)(nesvnic->nesdev->nesadapter->fw_ver >> 32),
+ (int)(nesvnic->nesdev->nesadapter->fw_ver >> 16) & 0xffff,
+ (int)(nesvnic->nesdev->nesadapter->fw_ver & 0xffff));
+}
+
+
+/**
+ * show_hca
+ */
+static ssize_t show_hca(struct class_device *cdev, char *buf)
+{
+ nes_debug(NES_DBG_INIT, "\n");
+ return sprintf(buf, "NES020\n");
+}
+
+
+/**
+ * show_board
+ */
+static ssize_t show_board(struct class_device *cdev, char *buf)
+{
+ nes_debug(NES_DBG_INIT, "\n");
+ return sprintf(buf, "%.*s\n", 32, "NES020 Board ID");
+}
+
+
+static CLASS_DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
+static CLASS_DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
+static CLASS_DEVICE_ATTR(hca_type, S_IRUGO, show_hca, NULL);
+static CLASS_DEVICE_ATTR(board_id, S_IRUGO, show_board, NULL);
+
+static struct class_device_attribute *nes_class_attributes[] = {
+ &class_device_attr_hw_rev,
+ &class_device_attr_fw_ver,
+ &class_device_attr_hca_type,
+ &class_device_attr_board_id
+};
+
+
+/**
+ * nes_query_qp
+ */
+static int nes_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+ int attr_mask, struct ib_qp_init_attr *init_attr)
+{
+ struct nes_qp *nesqp = to_nesqp(ibqp);
+
+ nes_debug(NES_DBG_QP, "\n");
+
+ attr->qp_access_flags = 0;
+ attr->cap.max_send_wr = nesqp->hwqp.sq_size;
+ attr->cap.max_recv_wr = nesqp->hwqp.rq_size;
+ attr->cap.max_recv_sge = 1;
+ if (nes_drv_opt & NES_DRV_OPT_NO_INLINE_DATA) {
+ init_attr->cap.max_inline_data = 0;
+ } else {
+ init_attr->cap.max_inline_data = 64;
+ }
+
+ init_attr->event_handler = nesqp->ibqp.event_handler;
+ init_attr->qp_context = nesqp->ibqp.qp_context;
+ init_attr->send_cq = nesqp->ibqp.send_cq;
+ init_attr->recv_cq = nesqp->ibqp.recv_cq;
+ init_attr->srq = nesqp->ibqp.srq = nesqp->ibqp.srq;
+ init_attr->cap = attr->cap;
+
+ return 0;
+}
+
+
+/**
+ * nes_hw_modify_qp
+ */
+int nes_hw_modify_qp(struct nes_device *nesdev, struct nes_qp *nesqp,
+ u32 next_iwarp_state, u32 wait_completion)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ /* struct iw_cm_id *cm_id = nesqp->cm_id; */
+ /* struct iw_cm_event cm_event; */
+ struct nes_cqp_request *cqp_request;
+ unsigned long flags;
+ int ret;
+ u16 major_code;
+
+ nes_debug(NES_DBG_MOD_QP, "QP%u, refcount=%d\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount));
+
+ cqp_request = nes_get_cqp_request(nesdev);
+ if (cqp_request == NULL) {
+ nes_debug(NES_DBG_MOD_QP, "Failed to get a cqp_request.\n");
+ return -ENOMEM;
+ }
+ if (wait_completion) {
+ cqp_request->waiting = 1;
+ } else {
+ cqp_request->waiting = 0;
+ }
+ cqp_wqe = &cqp_request->cqp_wqe;
+
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_OPCODE_IDX,
+ NES_CQP_MODIFY_QP | NES_CQP_QP_TYPE_IWARP | next_iwarp_state);
+ nes_debug(NES_DBG_MOD_QP, "using next_iwarp_state=%08x, wqe_words=%08x\n",
+ next_iwarp_state, le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX]));
+ nes_fill_init_cqp_wqe(cqp_wqe, nesdev);
+ set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
+ set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, (u64)nesqp->nesqp_context_pbase);
+
+ atomic_set(&cqp_request->refcount, 2);
+ nes_post_cqp_request(nesdev, cqp_request, NES_CQP_REQUEST_RING_DOORBELL);
+
+ /* Wait for CQP */
+ if (wait_completion) {
+ /* nes_debug(NES_DBG_MOD_QP, "Waiting for modify iWARP QP%u to complete.\n",
+ nesqp->hwqp.qp_id); */
+ ret = wait_event_timeout(cqp_request->waitq, (cqp_request->request_done != 0),
+ NES_EVENT_TIMEOUT);
+ nes_debug(NES_DBG_MOD_QP, "Modify iwarp QP%u completed, wait_event_timeout ret=%u, "
+ "CQP Major:Minor codes = 0x%04X:0x%04X.\n",
+ nesqp->hwqp.qp_id, ret, cqp_request->major_code, cqp_request->minor_code);
+ major_code = cqp_request->major_code;
+ if (major_code) {
+ nes_debug(NES_DBG_MOD_QP, "Modify iwarp QP%u failed"
+ "CQP Major:Minor codes = 0x%04X:0x%04X, intended next state = 0x%08X.\n",
+ nesqp->hwqp.qp_id, cqp_request->major_code,
+ cqp_request->minor_code, next_iwarp_state);
+ }
+ if (atomic_dec_and_test(&cqp_request->refcount)) {
+ if (cqp_request->dynamic) {
+ kfree(cqp_request);
+ } else {
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ }
+ }
+ if (!ret)
+ return -ETIME;
+ else if (major_code)
+ return -EIO;
+ else
+ return 0;
+ } else {
+ return 0;
+ }
+}
+
+
+/**
+ * nes_modify_qp
+ */
+int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+ int attr_mask, struct ib_udata *udata)
+{
+ struct nes_qp *nesqp = to_nesqp(ibqp);
+ struct nes_vnic *nesvnic = to_nesvnic(ibqp->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ /* u32 cqp_head; */
+ /* u32 counter; */
+ u32 next_iwarp_state = 0;
+ int err;
+ unsigned long qplockflags;
+ int ret;
+ u16 original_last_aeq;
+ u8 issue_modify_qp = 0;
+ u8 issue_disconnect = 0;
+ u8 dont_wait = 0;
+
+ nes_debug(NES_DBG_MOD_QP, "QP%u: QP State=%u, cur QP State=%u,"
+ " iwarp_state=0x%X, refcount=%d\n",
+ nesqp->hwqp.qp_id, attr->qp_state, nesqp->ibqp_state,
+ nesqp->iwarp_state, atomic_read(&nesqp->refcount));
+
+ nes_add_ref(&nesqp->ibqp);
+ spin_lock_irqsave(&nesqp->lock, qplockflags);
+
+ nes_debug(NES_DBG_MOD_QP, "QP%u: hw_iwarp_state=0x%X, hw_tcp_state=0x%X,"
+ " QP Access Flags=0x%X, attr_mask = 0x%0x\n",
+ nesqp->hwqp.qp_id, nesqp->hw_iwarp_state,
+ nesqp->hw_tcp_state, attr->qp_access_flags, attr_mask);
+
+ if (attr_mask & IB_QP_STATE) {
+ switch (attr->qp_state) {
+ case IB_QPS_INIT:
+ nes_debug(NES_DBG_MOD_QP, "QP%u: new state = init\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->iwarp_state > (u32)NES_CQP_QP_IWARP_STATE_IDLE) {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_IDLE;
+ issue_modify_qp = 1;
+ break;
+ case IB_QPS_RTR:
+ nes_debug(NES_DBG_MOD_QP, "QP%u: new state = rtr\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->iwarp_state>(u32)NES_CQP_QP_IWARP_STATE_IDLE) {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_IDLE;
+ issue_modify_qp = 1;
+ break;
+ case IB_QPS_RTS:
+ nes_debug(NES_DBG_MOD_QP, "QP%u: new state = rts\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->iwarp_state>(u32)NES_CQP_QP_IWARP_STATE_RTS) {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ if (nesqp->cm_id == NULL) {
+ nes_debug(NES_DBG_MOD_QP, "QP%u: Failing attempt to move QP to RTS without a CM_ID. \n",
+ nesqp->hwqp.qp_id );
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_RTS;
+ if (nesqp->iwarp_state != NES_CQP_QP_IWARP_STATE_RTS)
+ next_iwarp_state |= NES_CQP_QP_CONTEXT_VALID |
+ NES_CQP_QP_ARP_VALID | NES_CQP_QP_ORD_VALID;
+ issue_modify_qp = 1;
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_ESTABLISHED;
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_RTS;
+ nesqp->hte_added = 1;
+ break;
+ case IB_QPS_SQD:
+ issue_modify_qp = 1;
+ nes_debug(NES_DBG_MOD_QP, "QP%u: new state=closing. SQ head=%u, SQ tail=%u\n",
+ nesqp->hwqp.qp_id, nesqp->hwqp.sq_head, nesqp->hwqp.sq_tail);
+ if (nesqp->iwarp_state == (u32)NES_CQP_QP_IWARP_STATE_CLOSING) {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return 0;
+ } else {
+ if (nesqp->iwarp_state > (u32)NES_CQP_QP_IWARP_STATE_CLOSING) {
+ nes_debug(NES_DBG_MOD_QP, "QP%u: State change to closing"
+ " ignored due to current iWARP state\n",
+ nesqp->hwqp.qp_id);
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ if (nesqp->hw_iwarp_state != NES_AEQE_IWARP_STATE_RTS) {
+ nes_debug(NES_DBG_MOD_QP, "QP%u: State change to closing"
+ " already done based on hw state.\n",
+ nesqp->hwqp.qp_id);
+ issue_modify_qp = 0;
+ nesqp->in_disconnect = 0;
+ }
+ switch (nesqp->hw_iwarp_state) {
+ case NES_AEQE_IWARP_STATE_CLOSING:
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_CLOSING;
+ case NES_AEQE_IWARP_STATE_TERMINATE:
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE;
+ break;
+ case NES_AEQE_IWARP_STATE_ERROR:
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR;
+ break;
+ default:
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_CLOSING;
+ nesqp->in_disconnect = 1;
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING;
+ break;
+ }
+ }
+ break;
+ case IB_QPS_SQE:
+ nes_debug(NES_DBG_MOD_QP, "QP%u: new state = terminate\n",
+ nesqp->hwqp.qp_id);
+ if (nesqp->iwarp_state>=(u32)NES_CQP_QP_IWARP_STATE_TERMINATE) {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ /* next_iwarp_state = (NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000); */
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE;
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_TERMINATE;
+ issue_modify_qp = 1;
+ nesqp->in_disconnect = 1;
+ break;
+ case IB_QPS_ERR:
+ case IB_QPS_RESET:
+ if (nesqp->iwarp_state == (u32)NES_CQP_QP_IWARP_STATE_ERROR) {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ }
+ nes_debug(NES_DBG_MOD_QP, "QP%u: new state = error\n",
+ nesqp->hwqp.qp_id);
+ next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR;
+ /* next_iwarp_state = (NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000); */
+ if (nesqp->hte_added) {
+ nes_debug(NES_DBG_MOD_QP, "set CQP_QP_DEL_HTE\n");
+ next_iwarp_state |= NES_CQP_QP_DEL_HTE;
+ nesqp->hte_added = 0;
+ }
+ if ((nesqp->hw_tcp_state > NES_AEQE_TCP_STATE_CLOSED) &&
+ (nesqp->hw_tcp_state != NES_AEQE_TCP_STATE_TIME_WAIT)) {
+ next_iwarp_state |= NES_CQP_QP_RESET;
+ nesqp->in_disconnect = 1;
+ } else {
+ nes_debug(NES_DBG_MOD_QP, "QP%u NOT setting NES_CQP_QP_RESET since TCP state = %u\n",
+ nesqp->hwqp.qp_id, nesqp->hw_tcp_state);
+ dont_wait = 1;
+ }
+ issue_modify_qp = 1;
+ nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_ERROR;
+ break;
+ default:
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_rem_ref(&nesqp->ibqp);
+ return -EINVAL;
+ break;
+ }
+
+ nesqp->ibqp_state = attr->qp_state;
+ if (((nesqp->iwarp_state & NES_CQP_QP_IWARP_STATE_MASK) ==
+ (u32)NES_CQP_QP_IWARP_STATE_RTS) &&
+ ((next_iwarp_state & NES_CQP_QP_IWARP_STATE_MASK) >
+ (u32)NES_CQP_QP_IWARP_STATE_RTS)) {
+ nesqp->iwarp_state = next_iwarp_state & NES_CQP_QP_IWARP_STATE_MASK;
+ nes_debug(NES_DBG_MOD_QP, "Change nesqp->iwarp_state=%08x\n",
+ nesqp->iwarp_state);
+ issue_disconnect = 1;
+ } else {
+ nesqp->iwarp_state = next_iwarp_state & NES_CQP_QP_IWARP_STATE_MASK;
+ nes_debug(NES_DBG_MOD_QP, "Change nesqp->iwarp_state=%08x\n",
+ nesqp->iwarp_state);
+ }
+ }
+
+ if (attr_mask & IB_QP_ACCESS_FLAGS) {
+ if (attr->qp_access_flags & IB_ACCESS_LOCAL_WRITE) {
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_RDMA_WRITE_EN |
+ NES_QPCONTEXT_MISC_RDMA_READ_EN);
+ issue_modify_qp = 1;
+ }
+ if (attr->qp_access_flags & IB_ACCESS_REMOTE_WRITE) {
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_RDMA_WRITE_EN);
+ issue_modify_qp = 1;
+ }
+ if (attr->qp_access_flags & IB_ACCESS_REMOTE_READ) {
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_RDMA_READ_EN);
+ issue_modify_qp = 1;
+ }
+ if (attr->qp_access_flags & IB_ACCESS_MW_BIND) {
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_WBIND_EN);
+ issue_modify_qp = 1;
+ }
+
+ if (nesqp->user_mode) {
+ nesqp->nesqp_context->misc |= cpu_to_le32(NES_QPCONTEXT_MISC_RDMA_WRITE_EN |
+ NES_QPCONTEXT_MISC_RDMA_READ_EN);
+ issue_modify_qp = 1;
+ }
+ }
+
+ original_last_aeq = nesqp->last_aeq;
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+
+ nes_debug(NES_DBG_MOD_QP, "issue_modify_qp=%u\n", issue_modify_qp);
+
+ ret = 0;
+
+
+ if (issue_modify_qp) {
+ nes_debug(NES_DBG_MOD_QP, "call nes_hw_modify_qp\n");
+ ret = nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 1);
+ if (ret)
+ nes_debug(NES_DBG_MOD_QP, "nes_hw_modify_qp (next_iwarp_state = 0x%08X)"
+ " failed for QP%u.\n",
+ next_iwarp_state, nesqp->hwqp.qp_id);
+
+ }
+
+ if ((issue_modify_qp) && (nesqp->ibqp_state > IB_QPS_RTS)) {
+ nes_debug(NES_DBG_MOD_QP, "QP%u Issued ModifyQP refcount (%d),"
+ " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ original_last_aeq, nesqp->last_aeq);
+ if ((!ret) ||
+ ((original_last_aeq != NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) &&
+ (ret))) {
+ if (dont_wait) {
+ if (nesqp->cm_id && nesqp->hw_tcp_state != 0) {
+ nes_debug(NES_DBG_MOD_QP, "QP%u Queuing fake disconnect for QP refcount (%d),"
+ " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ original_last_aeq, nesqp->last_aeq);
+ /* this one is for the cm_disconnect thread */
+ nes_add_ref(&nesqp->ibqp);
+ spin_lock_irqsave(&nesqp->lock, qplockflags);
+ nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+ nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_cm_disconn(nesqp);
+ } else {
+ nes_debug(NES_DBG_MOD_QP, "QP%u No fake disconnect, QP refcount=%d\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount));
+ nes_rem_ref(&nesqp->ibqp);
+ }
+ } else {
+ spin_lock_irqsave(&nesqp->lock, qplockflags);
+ if (nesqp->cm_id) {
+ /* These two are for the timer thread */
+ if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
+ nes_add_ref(&nesqp->ibqp);
+ nesqp->cm_id->add_ref(nesqp->cm_id);
+ nes_debug(NES_DBG_MOD_QP, "QP%u Not decrementing QP refcount (%d),"
+ " need ae to finish up, original_last_aeq = 0x%04X."
+ " last_aeq = 0x%04X, scheduling timer.\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ original_last_aeq, nesqp->last_aeq);
+ schedule_nes_timer(nesqp->cm_node, (struct sk_buff *) nesqp, NES_TIMER_TYPE_CLOSE, 1, 0);
+ }
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ } else {
+ spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+ nes_debug(NES_DBG_MOD_QP, "QP%u Not decrementing QP refcount (%d),"
+ " need ae to finish up, original_last_aeq = 0x%04X."
+ " last_aeq = 0x%04X.\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ original_last_aeq, nesqp->last_aeq);
+ }
+ }
+ } else {
+ nes_debug(NES_DBG_MOD_QP, "QP%u Decrementing QP refcount (%d), No ae to finish up,"
+ " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ original_last_aeq, nesqp->last_aeq);
+ nes_rem_ref(&nesqp->ibqp);
+ }
+ } else {
+ nes_debug(NES_DBG_MOD_QP, "QP%u Decrementing QP refcount (%d), No ae to finish up,"
+ " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
+ original_last_aeq, nesqp->last_aeq);
+ nes_rem_ref(&nesqp->ibqp);
+ }
+
+ err = 0;
+
+ nes_debug(NES_DBG_MOD_QP, "QP%u Leaving, refcount=%d\n",
+ nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount));
+
+ return err;
+}
+
+
+/**
+ * nes_muticast_attach
+ */
+static int nes_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+ nes_debug(NES_DBG_INIT, "\n");
+ return -ENOSYS;
+}
+
+
+/**
+ * nes_multicast_detach
+ */
+static int nes_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+ nes_debug(NES_DBG_INIT, "\n");
+ return -ENOSYS;
+}
+
+
+/**
+ * nes_process_mad
+ */
+static int nes_process_mad(struct ib_device *ibdev, int mad_flags,
+ u8 port_num, struct ib_wc *in_wc, struct ib_grh *in_grh,
+ struct ib_mad *in_mad, struct ib_mad *out_mad)
+{
+ nes_debug(NES_DBG_INIT, "\n");
+ return -ENOSYS;
+}
+
+static inline void
+fill_wqe_sg_send(struct nes_hw_qp_wqe *wqe, struct ib_send_wr *ib_wr, u32 uselkey)
+{
+ int sge_index;
+ int total_payload_length = 0;
+ for (sge_index = 0; sge_index < ib_wr->num_sge; sge_index++) {
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_FRAG0_LOW_IDX+(sge_index*4),
+ ib_wr->sg_list[sge_index].addr);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_LENGTH0_IDX + (sge_index*4),
+ ib_wr->sg_list[sge_index].length);
+ if (uselkey)
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_STAG0_IDX + (sge_index*4),
+ (ib_wr->sg_list[sge_index].lkey));
+ else
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_STAG0_IDX + (sge_index*4), 0);
+
+ total_payload_length += ib_wr->sg_list[sge_index].length;
+ }
+ nes_debug(NES_DBG_IW_TX, "UC UC UC, sending total_payload_length=%u \n",
+ total_payload_length);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX,
+ total_payload_length);
+}
+
+/**
+ * nes_post_send
+ */
+static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
+ struct ib_send_wr **bad_wr)
+{
+ u64 u64temp;
+ unsigned long flags = 0;
+ struct nes_vnic *nesvnic = to_nesvnic(ibqp->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_qp *nesqp = to_nesqp(ibqp);
+ struct nes_hw_qp_wqe *wqe;
+ int err;
+ u32 qsize = nesqp->hwqp.sq_size;
+ u32 head;
+ u32 wqe_misc;
+ u32 wqe_count;
+ u32 counter;
+ u32 total_payload_length;
+
+ err = 0;
+ wqe_misc = 0;
+ wqe_count = 0;
+ total_payload_length = 0;
+
+ if (nesqp->ibqp_state > IB_QPS_RTS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&nesqp->lock, flags);
+
+ head = nesqp->hwqp.sq_head;
+
+ while (ib_wr) {
+ /* Check for SQ overflow */
+ if (((head + (2 * qsize) - nesqp->hwqp.sq_tail) % qsize) == (qsize - 1)) {
+ err = -EINVAL;
+ break;
+ }
+
+ wqe = &nesqp->hwqp.sq_vbase[head];
+ /* nes_debug(NES_DBG_IW_TX, "processing sq wqe for QP%u at %p, head = %u.\n",
+ nesqp->hwqp.qp_id, wqe, head); */
+ nes_fill_init_qp_wqe(wqe, nesqp, head);
+ u64temp = (u64)(ib_wr->wr_id);
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_SCRATCH_LOW_IDX,
+ u64temp);
+ switch (ib_wr->opcode) {
+ case IB_WR_SEND:
+ if (ib_wr->send_flags & IB_SEND_SOLICITED) {
+ wqe_misc = NES_IWARP_SQ_OP_SENDSE;
+ } else {
+ wqe_misc = NES_IWARP_SQ_OP_SEND;
+ }
+ if (ib_wr->num_sge > nesdev->nesadapter->max_sge) {
+ err = -EINVAL;
+ break;
+ }
+ if (ib_wr->send_flags & IB_SEND_FENCE) {
+ wqe_misc |= NES_IWARP_SQ_WQE_LOCAL_FENCE;
+ }
+ if ((ib_wr->send_flags & IB_SEND_INLINE) &&
+ ((nes_drv_opt & NES_DRV_OPT_NO_INLINE_DATA) == 0) &&
+ (ib_wr->sg_list[0].length <= 64)) {
+ memcpy(&wqe->wqe_words[NES_IWARP_SQ_WQE_IMM_DATA_START_IDX],
+ (void *)(unsigned long)ib_wr->sg_list[0].addr, ib_wr->sg_list[0].length);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX,
+ ib_wr->sg_list[0].length);
+ wqe_misc |= NES_IWARP_SQ_WQE_IMM_DATA;
+ } else {
+ fill_wqe_sg_send(wqe, ib_wr, 1);
+ }
+
+ break;
+ case IB_WR_RDMA_WRITE:
+ wqe_misc = NES_IWARP_SQ_OP_RDMAW;
+ if (ib_wr->num_sge > nesdev->nesadapter->max_sge) {
+ nes_debug(NES_DBG_IW_TX, "Exceeded max sge, ib_wr=%u, max=%u\n",
+ ib_wr->num_sge,
+ nesdev->nesadapter->max_sge);
+ err = -EINVAL;
+ break;
+ }
+ if (ib_wr->send_flags & IB_SEND_FENCE) {
+ wqe_misc |= NES_IWARP_SQ_WQE_LOCAL_FENCE;
+ }
+
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_RDMA_STAG_IDX,
+ ib_wr->wr.rdma.rkey);
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_RDMA_TO_LOW_IDX,
+ ib_wr->wr.rdma.remote_addr);
+
+ if ((ib_wr->send_flags & IB_SEND_INLINE) &&
+ ((nes_drv_opt & NES_DRV_OPT_NO_INLINE_DATA) == 0) &&
+ (ib_wr->sg_list[0].length <= 64)) {
+ memcpy(&wqe->wqe_words[NES_IWARP_SQ_WQE_IMM_DATA_START_IDX],
+ (void *)(unsigned long)ib_wr->sg_list[0].addr, ib_wr->sg_list[0].length);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX,
+ ib_wr->sg_list[0].length);
+ wqe_misc |= NES_IWARP_SQ_WQE_IMM_DATA;
+ } else {
+ fill_wqe_sg_send(wqe, ib_wr, 1);
+ }
+ wqe->wqe_words[NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX] =
+ wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX];
+ break;
+ case IB_WR_RDMA_READ:
+ /* iWARP only supports 1 sge for RDMA reads */
+ if (ib_wr->num_sge > 1) {
+ nes_debug(NES_DBG_IW_TX, "Exceeded max sge, ib_wr=%u, max=1\n",
+ ib_wr->num_sge);
+ err = -EINVAL;
+ break;
+ }
+ wqe_misc = NES_IWARP_SQ_OP_RDMAR;
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_RDMA_TO_LOW_IDX,
+ ib_wr->wr.rdma.remote_addr);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_RDMA_STAG_IDX,
+ ib_wr->wr.rdma.rkey);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX,
+ ib_wr->sg_list->length);
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_FRAG0_LOW_IDX,
+ ib_wr->sg_list->addr);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_STAG0_IDX,
+ ib_wr->sg_list->lkey);
+ break;
+ default:
+ /* error */
+ err = -EINVAL;
+ break;
+ }
+
+ if (ib_wr->send_flags & IB_SEND_SIGNALED) {
+ wqe_misc |= NES_IWARP_SQ_WQE_SIGNALED_COMPL;
+ }
+ wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = cpu_to_le32(wqe_misc);
+
+ ib_wr = ib_wr->next;
+ head++;
+ wqe_count++;
+ if (head >= qsize)
+ head = 0;
+
+ }
+
+ nesqp->hwqp.sq_head = head;
+ barrier();
+ while (wqe_count) {
+ counter = min(wqe_count, ((u32)255));
+ wqe_count -= counter;
+ nes_write32(nesdev->regs + NES_WQE_ALLOC,
+ (counter << 24) | 0x00800000 | nesqp->hwqp.qp_id);
+ }
+
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+
+ if (err)
+ *bad_wr = ib_wr;
+ return err;
+}
+
+
+/**
+ * nes_post_recv
+ */
+static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr,
+ struct ib_recv_wr **bad_wr)
+{
+ u64 u64temp;
+ unsigned long flags = 0;
+ struct nes_vnic *nesvnic = to_nesvnic(ibqp->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_qp *nesqp = to_nesqp(ibqp);
+ struct nes_hw_qp_wqe *wqe;
+ int err = 0;
+ int sge_index;
+ u32 qsize = nesqp->hwqp.rq_size;
+ u32 head;
+ u32 wqe_count = 0;
+ u32 counter;
+ u32 total_payload_length;
+
+ if (nesqp->ibqp_state > IB_QPS_RTS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&nesqp->lock, flags);
+
+ head = nesqp->hwqp.rq_head;
+
+ while (ib_wr) {
+ if (ib_wr->num_sge > nesdev->nesadapter->max_sge) {
+ err = -EINVAL;
+ break;
+ }
+ /* Check for RQ overflow */
+ if (((head + (2 * qsize) - nesqp->hwqp.rq_tail) % qsize) == (qsize - 1)) {
+ err = -EINVAL;
+ break;
+ }
+
+ nes_debug(NES_DBG_IW_RX, "ibwr sge count = %u.\n", ib_wr->num_sge);
+ wqe = &nesqp->hwqp.rq_vbase[head];
+
+ /* nes_debug(NES_DBG_IW_RX, "QP%u:processing rq wqe at %p, head = %u.\n",
+ nesqp->hwqp.qp_id, wqe, head); */
+ nes_fill_init_qp_wqe(wqe, nesqp, head);
+ u64temp = (u64)(ib_wr->wr_id);
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_SCRATCH_LOW_IDX,
+ u64temp);
+ total_payload_length = 0;
+ for (sge_index=0; sge_index < ib_wr->num_sge; sge_index++) {
+ set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_RQ_WQE_FRAG0_LOW_IDX+(sge_index*4),
+ ib_wr->sg_list[sge_index].addr);
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_RQ_WQE_LENGTH0_IDX+(sge_index*4),
+ ib_wr->sg_list[sge_index].length);
+ set_wqe_32bit_value(wqe->wqe_words,NES_IWARP_RQ_WQE_STAG0_IDX+(sge_index*4),
+ ib_wr->sg_list[sge_index].lkey);
+
+ total_payload_length += ib_wr->sg_list[sge_index].length;
+ }
+ set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_RQ_WQE_TOTAL_PAYLOAD_IDX,
+ total_payload_length);
+
+ ib_wr = ib_wr->next;
+ head++;
+ wqe_count++;
+ if (head >= qsize)
+ head = 0;
+ }
+
+ nesqp->hwqp.rq_head = head;
+ barrier();
+ while (wqe_count) {
+ counter = min(wqe_count, ((u32)255));
+ wqe_count -= counter;
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, (counter<<24) | nesqp->hwqp.qp_id);
+ }
+
+ spin_unlock_irqrestore(&nesqp->lock, flags);
+
+ if (err)
+ *bad_wr = ib_wr;
+ return err;
+}
+
+
+/**
+ * nes_poll_cq
+ */
+static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
+{
+ u64 u64temp;
+ u64 wrid;
+ /* u64 u64temp; */
+ unsigned long flags = 0;
+ struct nes_vnic *nesvnic = to_nesvnic(ibcq->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_cq *nescq = to_nescq(ibcq);
+ struct nes_qp *nesqp;
+ struct nes_hw_cqe cqe;
+ u32 head;
+ u32 wq_tail;
+ u32 cq_size;
+ u32 cqe_count = 0;
+ u32 wqe_index;
+ u32 u32temp;
+ /* u32 counter; */
+
+ nes_debug(NES_DBG_CQ, "\n");
+
+ spin_lock_irqsave(&nescq->lock, flags);
+
+ head = nescq->hw_cq.cq_head;
+ cq_size = nescq->hw_cq.cq_size;
+
+ while (cqe_count < num_entries) {
+ if (le32_to_cpu(nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) &
+ NES_CQE_VALID) {
+ cqe = nescq->hw_cq.cq_vbase[head];
+ nescq->hw_cq.cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX] = 0;
+ u32temp = le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]);
+ wqe_index = u32temp &
+ (nesdev->nesadapter->max_qp_wr - 1);
+ u32temp &= ~(NES_SW_CONTEXT_ALIGN-1);
+ /* parse CQE, get completion context from WQE (either rq or sq */
+ u64temp = (((u64)(le32_to_cpu(cqe.cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX])))<<32) |
+ ((u64)u32temp);
+ nesqp = *((struct nes_qp **)&u64temp);
+ memset(entry, 0, sizeof *entry);
+ if (cqe.cqe_words[NES_CQE_ERROR_CODE_IDX] == 0) {
+ entry->status = IB_WC_SUCCESS;
+ } else {
+ entry->status = IB_WC_WR_FLUSH_ERR;
+ }
+
+ entry->qp = &nesqp->ibqp;
+ entry->src_qp = nesqp->hwqp.qp_id;
+
+ if (le32_to_cpu(cqe.cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_SQ) {
+ if (nesqp->skip_lsmm) {
+ nesqp->skip_lsmm = 0;
+ wq_tail = nesqp->hwqp.sq_tail++;
+ }
+
+ /* Working on a SQ Completion*/
+ wq_tail = wqe_index;
+ nesqp->hwqp.sq_tail = (wqe_index+1)&(nesqp->hwqp.sq_size - 1);
+ wrid = (((u64)(cpu_to_le32((u32)nesqp->hwqp.sq_vbase[wq_tail].
+ wqe_words[NES_IWARP_SQ_WQE_COMP_SCRATCH_HIGH_IDX]))) << 32) |
+ ((u64)(cpu_to_le32((u32)nesqp->hwqp.sq_vbase[wq_tail].
+ wqe_words[NES_IWARP_SQ_WQE_COMP_SCRATCH_LOW_IDX])));
+ entry->byte_len = le32_to_cpu(nesqp->hwqp.sq_vbase[wq_tail].
+ wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX]);
+
+ switch (le32_to_cpu(nesqp->hwqp.sq_vbase[wq_tail].
+ wqe_words[NES_IWARP_SQ_WQE_MISC_IDX]) & 0x3f) {
+ case NES_IWARP_SQ_OP_RDMAW:
+ nes_debug(NES_DBG_CQ, "Operation = RDMA WRITE.\n");
+ entry->opcode = IB_WC_RDMA_WRITE;
+ break;
+ case NES_IWARP_SQ_OP_RDMAR:
+ nes_debug(NES_DBG_CQ, "Operation = RDMA READ.\n");
+ entry->opcode = IB_WC_RDMA_READ;
+ entry->byte_len = le32_to_cpu(nesqp->hwqp.sq_vbase[wq_tail].
+ wqe_words[NES_IWARP_SQ_WQE_RDMA_LENGTH_IDX]);
+ break;
+ case NES_IWARP_SQ_OP_SENDINV:
+ case NES_IWARP_SQ_OP_SENDSEINV:
+ case NES_IWARP_SQ_OP_SEND:
+ case NES_IWARP_SQ_OP_SENDSE:
+ nes_debug(NES_DBG_CQ, "Operation = Send.\n");
+ entry->opcode = IB_WC_SEND;
+ break;
+ }
+ } else {
+ /* Working on a RQ Completion*/
+ wq_tail = wqe_index;
+ nesqp->hwqp.rq_tail = (wqe_index+1)&(nesqp->hwqp.rq_size - 1);
+ entry->byte_len = le32_to_cpu(cqe.cqe_words[NES_CQE_PAYLOAD_LENGTH_IDX]);
+ wrid = ((u64)(le32_to_cpu(nesqp->hwqp.rq_vbase[wq_tail].wqe_words[NES_IWARP_RQ_WQE_COMP_SCRATCH_LOW_IDX]))) |
+ ((u64)(le32_to_cpu(nesqp->hwqp.rq_vbase[wq_tail].wqe_words[NES_IWARP_RQ_WQE_COMP_SCRATCH_HIGH_IDX]))<<32);
+ entry->opcode = IB_WC_RECV;
+ }
+ entry->wr_id = wrid;
+
+ if (++head >= cq_size)
+ head = 0;
+ cqe_count++;
+ nescq->polled_completions++;
+ if ((nescq->polled_completions > (cq_size / 2)) ||
+ (nescq->polled_completions == 255)) {
+ nes_debug(NES_DBG_CQ, "CQ%u Issuing CQE Allocate since more than half of cqes"
+ " are pending %u of %u.\n",
+ nescq->hw_cq.cq_number, nescq->polled_completions, cq_size);
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
+ nescq->hw_cq.cq_number | (nescq->polled_completions << 16));
+ nescq->polled_completions = 0;
+ }
+ entry++;
+ } else
+ break;
+ }
+
+ if (nescq->polled_completions) {
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
+ nescq->hw_cq.cq_number | (nescq->polled_completions << 16));
+ nescq->polled_completions = 0;
+ }
+
+ nescq->hw_cq.cq_head = head;
+ nes_debug(NES_DBG_CQ, "Reporting %u completions for CQ%u.\n",
+ cqe_count, nescq->hw_cq.cq_number);
+
+ spin_unlock_irqrestore(&nescq->lock, flags);
+
+ return cqe_count;
+}
+
+
+/**
+ * nes_req_notify_cq
+ */
+static int nes_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags)
+ {
+ struct nes_vnic *nesvnic = to_nesvnic(ibcq->device);
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_cq *nescq = to_nescq(ibcq);
+ u32 cq_arm;
+
+ nes_debug(NES_DBG_CQ, "Requesting notification for CQ%u.\n",
+ nescq->hw_cq.cq_number);
+
+ cq_arm = nescq->hw_cq.cq_number;
+ if ((notify_flags & IB_CQ_SOLICITED_MASK) == IB_CQ_NEXT_COMP)
+ cq_arm |= NES_CQE_ALLOC_NOTIFY_NEXT;
+ else if ((notify_flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED)
+ cq_arm |= NES_CQE_ALLOC_NOTIFY_SE;
+ else
+ return -EINVAL;
+
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, cq_arm);
+ nes_read32(nesdev->regs+NES_CQE_ALLOC);
+
+ return 0;
+}
+
+
+/**
+ * nes_init_ofa_device
+ */
+struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
+{
+ struct nes_ib_device *nesibdev;
+ struct nes_vnic *nesvnic = netdev_priv(netdev);
+ struct nes_device *nesdev = nesvnic->nesdev;
+
+ nesibdev = (struct nes_ib_device *)ib_alloc_device(sizeof(struct nes_ib_device));
+ if (nesibdev == NULL) {
+ return NULL;
+ }
+ strlcpy(nesibdev->ibdev.name, "nes%d", IB_DEVICE_NAME_MAX);
+ nesibdev->ibdev.owner = THIS_MODULE;
+
+ nesibdev->ibdev.node_type = RDMA_NODE_RNIC;
+ memset(&nesibdev->ibdev.node_guid, 0, sizeof(nesibdev->ibdev.node_guid));
+ memcpy(&nesibdev->ibdev.node_guid, netdev->dev_addr, 6);
+
+ nesibdev->ibdev.uverbs_cmd_mask =
+ (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) |
+ (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) |
+ (1ull << IB_USER_VERBS_CMD_QUERY_PORT) |
+ (1ull << IB_USER_VERBS_CMD_ALLOC_PD) |
+ (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) |
+ (1ull << IB_USER_VERBS_CMD_REG_MR) |
+ (1ull << IB_USER_VERBS_CMD_DEREG_MR) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_CQ) |
+ (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_AH) |
+ (1ull << IB_USER_VERBS_CMD_DESTROY_AH) |
+ (1ull << IB_USER_VERBS_CMD_REQ_NOTIFY_CQ) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_QP) |
+ (1ull << IB_USER_VERBS_CMD_MODIFY_QP) |
+ (1ull << IB_USER_VERBS_CMD_POLL_CQ) |
+ (1ull << IB_USER_VERBS_CMD_DESTROY_QP) |
+ (1ull << IB_USER_VERBS_CMD_ALLOC_MW) |
+ (1ull << IB_USER_VERBS_CMD_BIND_MW) |
+ (1ull << IB_USER_VERBS_CMD_DEALLOC_MW) |
+ (1ull << IB_USER_VERBS_CMD_POST_RECV) |
+ (1ull << IB_USER_VERBS_CMD_POST_SEND);
+
+ nesibdev->ibdev.phys_port_cnt = 1;
+ nesibdev->ibdev.num_comp_vectors = 1;
+ nesibdev->ibdev.dma_device = &nesdev->pcidev->dev;
+ nesibdev->ibdev.class_dev.dev = &nesdev->pcidev->dev;
+ nesibdev->ibdev.query_device = nes_query_device;
+ nesibdev->ibdev.query_port = nes_query_port;
+ nesibdev->ibdev.modify_port = nes_modify_port;
+ nesibdev->ibdev.query_pkey = nes_query_pkey;
+ nesibdev->ibdev.query_gid = nes_query_gid;
+ nesibdev->ibdev.alloc_ucontext = nes_alloc_ucontext;
+ nesibdev->ibdev.dealloc_ucontext = nes_dealloc_ucontext;
+ nesibdev->ibdev.mmap = nes_mmap;
+ nesibdev->ibdev.alloc_pd = nes_alloc_pd;
+ nesibdev->ibdev.dealloc_pd = nes_dealloc_pd;
+ nesibdev->ibdev.create_ah = nes_create_ah;
+ nesibdev->ibdev.destroy_ah = nes_destroy_ah;
+ nesibdev->ibdev.create_qp = nes_create_qp;
+ nesibdev->ibdev.modify_qp = nes_modify_qp;
+ nesibdev->ibdev.query_qp = nes_query_qp;
+ nesibdev->ibdev.destroy_qp = nes_destroy_qp;
+ nesibdev->ibdev.create_cq = nes_create_cq;
+ nesibdev->ibdev.destroy_cq = nes_destroy_cq;
+ nesibdev->ibdev.poll_cq = nes_poll_cq;
+ nesibdev->ibdev.get_dma_mr = nes_get_dma_mr;
+ nesibdev->ibdev.reg_phys_mr = nes_reg_phys_mr;
+ nesibdev->ibdev.reg_user_mr = nes_reg_user_mr;
+ nesibdev->ibdev.dereg_mr = nes_dereg_mr;
+ nesibdev->ibdev.alloc_mw = nes_alloc_mw;
+ nesibdev->ibdev.dealloc_mw = nes_dealloc_mw;
+ nesibdev->ibdev.bind_mw = nes_bind_mw;
+
+ nesibdev->ibdev.alloc_fmr = nes_alloc_fmr;
+ nesibdev->ibdev.unmap_fmr = nes_unmap_fmr;
+ nesibdev->ibdev.dealloc_fmr = nes_dealloc_fmr;
+ nesibdev->ibdev.map_phys_fmr = nes_map_phys_fmr;
+
+ nesibdev->ibdev.attach_mcast = nes_multicast_attach;
+ nesibdev->ibdev.detach_mcast = nes_multicast_detach;
+ nesibdev->ibdev.process_mad = nes_process_mad;
+
+ nesibdev->ibdev.req_notify_cq = nes_req_notify_cq;
+ nesibdev->ibdev.post_send = nes_post_send;
+ nesibdev->ibdev.post_recv = nes_post_recv;
+
+ nesibdev->ibdev.iwcm = kzalloc(sizeof(*nesibdev->ibdev.iwcm), GFP_KERNEL);
+ if (nesibdev->ibdev.iwcm == NULL) {
+ ib_dealloc_device(&nesibdev->ibdev);
+ return NULL;
+ }
+ nesibdev->ibdev.iwcm->add_ref = nes_add_ref;
+ nesibdev->ibdev.iwcm->rem_ref = nes_rem_ref;
+ nesibdev->ibdev.iwcm->get_qp = nes_get_qp;
+ nesibdev->ibdev.iwcm->connect = nes_connect;
+ nesibdev->ibdev.iwcm->accept = nes_accept;
+ nesibdev->ibdev.iwcm->reject = nes_reject;
+ nesibdev->ibdev.iwcm->create_listen = nes_create_listen;
+ nesibdev->ibdev.iwcm->destroy_listen = nes_destroy_listen;
+
+ return nesibdev;
+}
+
+
+/**
+ * nes_destroy_ofa_device
+ */
+void nes_destroy_ofa_device(struct nes_ib_device *nesibdev)
+{
+ if (nesibdev == NULL)
+ return;
+
+ nes_unregister_ofa_device(nesibdev);
+
+ kfree(nesibdev->ibdev.iwcm);
+ ib_dealloc_device(&nesibdev->ibdev);
+}
+
+
+/**
+ * nes_register_ofa_device
+ */
+int nes_register_ofa_device(struct nes_ib_device *nesibdev)
+{
+ struct nes_vnic *nesvnic = nesibdev->nesvnic;
+ struct nes_device *nesdev = nesvnic->nesdev;
+ struct nes_adapter *nesadapter = nesdev->nesadapter;
+ int i, ret;
+
+ ret = ib_register_device(&nesvnic->nesibdev->ibdev);
+ if (ret) {
+ return ret;
+ }
+
+ /* Get the resources allocated to this device */
+ nesibdev->max_cq = (nesadapter->max_cq-NES_FIRST_QPN) / nesadapter->port_count;
+ nesibdev->max_mr = nesadapter->max_mr / nesadapter->port_count;
+ nesibdev->max_qp = (nesadapter->max_qp-NES_FIRST_QPN) / nesadapter->port_count;
+ nesibdev->max_pd = nesadapter->max_pd / nesadapter->port_count;
+
+ for (i = 0; i < ARRAY_SIZE(nes_class_attributes); ++i) {
+ ret = class_device_create_file(&nesibdev->ibdev.class_dev, nes_class_attributes[i]);
+ if (ret) {
+ while (i > 0) {
+ i--;
+ class_device_remove_file(&nesibdev->ibdev.class_dev,
+ nes_class_attributes[i]);
+ }
+ ib_unregister_device(&nesibdev->ibdev);
+ return ret;
+ }
+ }
+
+ nesvnic->of_device_registered = 1;
+
+ return 0;
+}
+
+
+/**
+ * nes_unregister_ofa_device
+ */
+void nes_unregister_ofa_device(struct nes_ib_device *nesibdev)
+{
+ struct nes_vnic *nesvnic = nesibdev->nesvnic;
+ int i;
+
+ if (nesibdev == NULL)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(nes_class_attributes); ++i) {
+ class_device_remove_file(&nesibdev->ibdev.class_dev, nes_class_attributes[i]);
+ }
+
+ if (nesvnic->of_device_registered) {
+ ib_unregister_device(&nesibdev->ibdev);
+ }
+
+ nesvnic->of_device_registered = 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 - 2008 NetEffect, Inc. All rights reserved.
+ * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef NES_VERBS_H
+#define NES_VERBS_H
+
+struct nes_device;
+
+#define NES_MAX_USER_DB_REGIONS 4096
+#define NES_MAX_USER_WQ_REGIONS 4096
+
+struct nes_ucontext {
+ struct ib_ucontext ibucontext;
+ struct nes_device *nesdev;
+ unsigned long mmap_wq_offset;
+ unsigned long mmap_cq_offset; /* to be removed */
+ int index; /* rnic index (minor) */
+ unsigned long allocated_doorbells[BITS_TO_LONGS(NES_MAX_USER_DB_REGIONS)];
+ u16 mmap_db_index[NES_MAX_USER_DB_REGIONS];
+ u16 first_free_db;
+ unsigned long allocated_wqs[BITS_TO_LONGS(NES_MAX_USER_WQ_REGIONS)];
+ struct nes_qp *mmap_nesqp[NES_MAX_USER_WQ_REGIONS];
+ u16 first_free_wq;
+ struct list_head cq_reg_mem_list;
+ struct list_head qp_reg_mem_list;
+ u32 mcrqf;
+ atomic_t usecnt;
+};
+
+struct nes_pd {
+ struct ib_pd ibpd;
+ u16 pd_id;
+ atomic_t sqp_count;
+ u16 mmap_db_index;
+};
+
+struct nes_mr {
+ union {
+ struct ib_mr ibmr;
+ struct ib_mw ibmw;
+ struct ib_fmr ibfmr;
+ };
+ struct ib_umem *region;
+ u16 pbls_used;
+ u8 mode;
+ u8 pbl_4k;
+};
+
+struct nes_hw_pb {
+ __le32 pa_low;
+ __le32 pa_high;
+};
+
+struct nes_vpbl {
+ dma_addr_t pbl_pbase;
+ struct nes_hw_pb *pbl_vbase;
+};
+
+struct nes_root_vpbl {
+ dma_addr_t pbl_pbase;
+ struct nes_hw_pb *pbl_vbase;
+ struct nes_vpbl *leaf_vpbl;
+};
+
+struct nes_fmr {
+ struct nes_mr nesmr;
+ u32 leaf_pbl_cnt;
+ struct nes_root_vpbl root_vpbl;
+ struct ib_qp *ib_qp;
+ int access_rights;
+ struct ib_fmr_attr attr;
+};
+
+struct nes_av;
+
+struct nes_cq {
+ struct ib_cq ibcq;
+ struct nes_hw_cq hw_cq;
+ u32 polled_completions;
+ u32 cq_mem_size;
+ spinlock_t lock;
+ u8 virtual_cq;
+ u8 pad[3];
+};
+
+struct nes_wq {
+ spinlock_t lock;
+};
+
+struct iw_cm_id;
+struct ietf_mpa_frame;
+
+struct nes_qp {
+ struct ib_qp ibqp;
+ void *allocated_buffer;
+ struct iw_cm_id *cm_id;
+ struct workqueue_struct *wq;
+ struct work_struct disconn_work;
+ struct nes_cq *nesscq;
+ struct nes_cq *nesrcq;
+ struct nes_pd *nespd;
+ void *cm_node; /* handle of the node this QP is associated with */
+ struct ietf_mpa_frame *ietf_frame;
+ dma_addr_t ietf_frame_pbase;
+ wait_queue_head_t state_waitq;
+ unsigned long socket;
+ struct nes_hw_qp hwqp;
+ struct work_struct work;
+ struct work_struct ae_work;
+ enum ib_qp_state ibqp_state;
+ u32 iwarp_state;
+ u32 hte_index;
+ u32 last_aeq;
+ u32 qp_mem_size;
+ atomic_t refcount;
+ atomic_t close_timer_started;
+ u32 mmap_sq_db_index;
+ u32 mmap_rq_db_index;
+ spinlock_t lock;
+ struct nes_qp_context *nesqp_context;
+ dma_addr_t nesqp_context_pbase;
+ void *pbl_vbase;
+ dma_addr_t pbl_pbase;
+ struct page *page;
+ wait_queue_head_t kick_waitq;
+ u16 in_disconnect;
+ u16 private_data_len;
+ u8 active_conn;
+ u8 skip_lsmm;
+ u8 user_mode;
+ u8 hte_added;
+ u8 hw_iwarp_state;
+ u8 flush_issued;
+ u8 hw_tcp_state;
+ u8 disconn_pending;
+ u8 destroyed;
+};
+#endif /* NES_VERBS_H */