RDMA/amso1100: Add driver for Ammasso 1100 RNIC
authorTom Tucker <tom@opengridcomputing.com>
Fri, 22 Sep 2006 22:22:48 +0000 (15:22 -0700)
committerRoland Dreier <rolandd@cisco.com>
Fri, 22 Sep 2006 22:22:48 +0000 (15:22 -0700)
Add a driver for the Ammasso 1100 gigabit ethernet RNIC.

Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
26 files changed:
MAINTAINERS
drivers/infiniband/Kconfig
drivers/infiniband/Makefile
drivers/infiniband/hw/amso1100/Kbuild [new file with mode: 0644]
drivers/infiniband/hw/amso1100/Kconfig [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_ae.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_ae.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_alloc.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_cm.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_cq.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_intr.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_mm.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_mq.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_mq.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_pd.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_provider.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_provider.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_qp.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_rnic.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_status.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_user.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_vq.c [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_vq.h [new file with mode: 0644]
drivers/infiniband/hw/amso1100/c2_wr.h [new file with mode: 0644]

index 830bec779d4791ec8e969d55d7efc2fc357c9562..b08c537018de214f2a7147ebaee36ff428a58dec 100644 (file)
@@ -298,6 +298,14 @@ L: info-linux@geode.amd.com
 W:     http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html
 S:     Supported
 
+AMSO1100 RNIC DRIVER
+P:     Tom Tucker
+M:     tom@opengridcomputing.com
+P:     Steve Wise
+M:     swise@opengridcomputing.com
+L:     openib-general@openib.org
+S:     Maintained
+
 AOA (Apple Onboard Audio) ALSA DRIVER
 P:     Johannes Berg
 M:     johannes@sipsolutions.net
index 9a329b2c108cc305ae633cacf95620b566d9da30..9edfacee7d8442cb6e5c49db23d493829991d540 100644 (file)
@@ -37,6 +37,7 @@ config INFINIBAND_ADDR_TRANS
 source "drivers/infiniband/hw/mthca/Kconfig"
 source "drivers/infiniband/hw/ipath/Kconfig"
 source "drivers/infiniband/hw/ehca/Kconfig"
+source "drivers/infiniband/hw/amso1100/Kconfig"
 
 source "drivers/infiniband/ulp/ipoib/Kconfig"
 
index 08cff32d900eab00d2fc00263dc2e576d35ac01d..2b5d1098ef45f4c3ba9fa5ccfed4c2aa76b0e7c3 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_INFINIBAND)                += core/
 obj-$(CONFIG_INFINIBAND_MTHCA)         += hw/mthca/
 obj-$(CONFIG_INFINIBAND_IPATH)         += hw/ipath/
 obj-$(CONFIG_INFINIBAND_EHCA)          += hw/ehca/
+obj-$(CONFIG_INFINIBAND_AMSO1100)      += hw/amso1100/
 obj-$(CONFIG_INFINIBAND_IPOIB)         += ulp/ipoib/
 obj-$(CONFIG_INFINIBAND_SRP)           += ulp/srp/
 obj-$(CONFIG_INFINIBAND_ISER)          += ulp/iser/
diff --git a/drivers/infiniband/hw/amso1100/Kbuild b/drivers/infiniband/hw/amso1100/Kbuild
new file mode 100644 (file)
index 0000000..06964c4
--- /dev/null
@@ -0,0 +1,8 @@
+ifdef CONFIG_INFINIBAND_AMSO1100_DEBUG
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_INFINIBAND_AMSO1100) += iw_c2.o
+
+iw_c2-y := c2.o c2_provider.o c2_rnic.o c2_alloc.o c2_mq.o c2_ae.o c2_vq.o \
+       c2_intr.o c2_cq.o c2_qp.o c2_cm.o c2_mm.o c2_pd.o
diff --git a/drivers/infiniband/hw/amso1100/Kconfig b/drivers/infiniband/hw/amso1100/Kconfig
new file mode 100644 (file)
index 0000000..809cb14
--- /dev/null
@@ -0,0 +1,15 @@
+config INFINIBAND_AMSO1100
+       tristate "Ammasso 1100 HCA support"
+       depends on PCI && INET && INFINIBAND
+       ---help---
+         This is a low-level driver for the Ammasso 1100 host
+         channel adapter (HCA).
+
+config INFINIBAND_AMSO1100_DEBUG
+       bool "Verbose debugging output"
+       depends on INFINIBAND_AMSO1100
+       default n
+       ---help---
+         This option causes the amso1100 driver to produce a bunch of
+         debug messages.  Select this if you are developing the driver
+         or trying to diagnose a problem.
diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c
new file mode 100644 (file)
index 0000000..9e9120f
--- /dev/null
@@ -0,0 +1,1255 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/delay.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 <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+#include <rdma/ib_smi.h>
+#include "c2.h"
+#include "c2_provider.h"
+
+MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
+MODULE_DESCRIPTION("Ammasso AMSO1100 Low-level iWARP Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRV_VERSION);
+
+static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
+    | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
+
+static int debug = -1;         /* defaults above */
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+static int c2_up(struct net_device *netdev);
+static int c2_down(struct net_device *netdev);
+static int c2_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
+static void c2_tx_interrupt(struct net_device *netdev);
+static void c2_rx_interrupt(struct net_device *netdev);
+static irqreturn_t c2_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void c2_tx_timeout(struct net_device *netdev);
+static int c2_change_mtu(struct net_device *netdev, int new_mtu);
+static void c2_reset(struct c2_port *c2_port);
+static struct net_device_stats *c2_get_stats(struct net_device *netdev);
+
+static struct pci_device_id c2_pci_table[] = {
+       { PCI_DEVICE(0x18b8, 0xb001) },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, c2_pci_table);
+
+static void c2_print_macaddr(struct net_device *netdev)
+{
+       pr_debug("%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);
+}
+
+static void c2_set_rxbufsize(struct c2_port *c2_port)
+{
+       struct net_device *netdev = c2_port->netdev;
+
+       if (netdev->mtu > RX_BUF_SIZE)
+               c2_port->rx_buf_size =
+                   netdev->mtu + ETH_HLEN + sizeof(struct c2_rxp_hdr) +
+                   NET_IP_ALIGN;
+       else
+               c2_port->rx_buf_size = sizeof(struct c2_rxp_hdr) + RX_BUF_SIZE;
+}
+
+/*
+ * Allocate TX ring elements and chain them together.
+ * One-to-one association of adapter descriptors with ring elements.
+ */
+static int c2_tx_ring_alloc(struct c2_ring *tx_ring, void *vaddr,
+                           dma_addr_t base, void __iomem * mmio_txp_ring)
+{
+       struct c2_tx_desc *tx_desc;
+       struct c2_txp_desc __iomem *txp_desc;
+       struct c2_element *elem;
+       int i;
+
+       tx_ring->start = kmalloc(sizeof(*elem) * tx_ring->count, GFP_KERNEL);
+       if (!tx_ring->start)
+               return -ENOMEM;
+
+       elem = tx_ring->start;
+       tx_desc = vaddr;
+       txp_desc = mmio_txp_ring;
+       for (i = 0; i < tx_ring->count; i++, elem++, tx_desc++, txp_desc++) {
+               tx_desc->len = 0;
+               tx_desc->status = 0;
+
+               /* Set TXP_HTXD_UNINIT */
+               __raw_writeq(cpu_to_be64(0x1122334455667788ULL),
+                            (void __iomem *) txp_desc + C2_TXP_ADDR);
+               __raw_writew(0, (void __iomem *) txp_desc + C2_TXP_LEN);
+               __raw_writew(cpu_to_be16(TXP_HTXD_UNINIT),
+                            (void __iomem *) txp_desc + C2_TXP_FLAGS);
+
+               elem->skb = NULL;
+               elem->ht_desc = tx_desc;
+               elem->hw_desc = txp_desc;
+
+               if (i == tx_ring->count - 1) {
+                       elem->next = tx_ring->start;
+                       tx_desc->next_offset = base;
+               } else {
+                       elem->next = elem + 1;
+                       tx_desc->next_offset =
+                           base + (i + 1) * sizeof(*tx_desc);
+               }
+       }
+
+       tx_ring->to_use = tx_ring->to_clean = tx_ring->start;
+
+       return 0;
+}
+
+/*
+ * Allocate RX ring elements and chain them together.
+ * One-to-one association of adapter descriptors with ring elements.
+ */
+static int c2_rx_ring_alloc(struct c2_ring *rx_ring, void *vaddr,
+                           dma_addr_t base, void __iomem * mmio_rxp_ring)
+{
+       struct c2_rx_desc *rx_desc;
+       struct c2_rxp_desc __iomem *rxp_desc;
+       struct c2_element *elem;
+       int i;
+
+       rx_ring->start = kmalloc(sizeof(*elem) * rx_ring->count, GFP_KERNEL);
+       if (!rx_ring->start)
+               return -ENOMEM;
+
+       elem = rx_ring->start;
+       rx_desc = vaddr;
+       rxp_desc = mmio_rxp_ring;
+       for (i = 0; i < rx_ring->count; i++, elem++, rx_desc++, rxp_desc++) {
+               rx_desc->len = 0;
+               rx_desc->status = 0;
+
+               /* Set RXP_HRXD_UNINIT */
+               __raw_writew(cpu_to_be16(RXP_HRXD_OK),
+                      (void __iomem *) rxp_desc + C2_RXP_STATUS);
+               __raw_writew(0, (void __iomem *) rxp_desc + C2_RXP_COUNT);
+               __raw_writew(0, (void __iomem *) rxp_desc + C2_RXP_LEN);
+               __raw_writeq(cpu_to_be64(0x99aabbccddeeffULL),
+                            (void __iomem *) rxp_desc + C2_RXP_ADDR);
+               __raw_writew(cpu_to_be16(RXP_HRXD_UNINIT),
+                            (void __iomem *) rxp_desc + C2_RXP_FLAGS);
+
+               elem->skb = NULL;
+               elem->ht_desc = rx_desc;
+               elem->hw_desc = rxp_desc;
+
+               if (i == rx_ring->count - 1) {
+                       elem->next = rx_ring->start;
+                       rx_desc->next_offset = base;
+               } else {
+                       elem->next = elem + 1;
+                       rx_desc->next_offset =
+                           base + (i + 1) * sizeof(*rx_desc);
+               }
+       }
+
+       rx_ring->to_use = rx_ring->to_clean = rx_ring->start;
+
+       return 0;
+}
+
+/* Setup buffer for receiving */
+static inline int c2_rx_alloc(struct c2_port *c2_port, struct c2_element *elem)
+{
+       struct c2_dev *c2dev = c2_port->c2dev;
+       struct c2_rx_desc *rx_desc = elem->ht_desc;
+       struct sk_buff *skb;
+       dma_addr_t mapaddr;
+       u32 maplen;
+       struct c2_rxp_hdr *rxp_hdr;
+
+       skb = dev_alloc_skb(c2_port->rx_buf_size);
+       if (unlikely(!skb)) {
+               pr_debug("%s: out of memory for receive\n",
+                       c2_port->netdev->name);
+               return -ENOMEM;
+       }
+
+       /* Zero out the rxp hdr in the sk_buff */
+       memset(skb->data, 0, sizeof(*rxp_hdr));
+
+       skb->dev = c2_port->netdev;
+
+       maplen = c2_port->rx_buf_size;
+       mapaddr =
+           pci_map_single(c2dev->pcidev, skb->data, maplen,
+                          PCI_DMA_FROMDEVICE);
+
+       /* Set the sk_buff RXP_header to RXP_HRXD_READY */
+       rxp_hdr = (struct c2_rxp_hdr *) skb->data;
+       rxp_hdr->flags = RXP_HRXD_READY;
+
+       __raw_writew(0, elem->hw_desc + C2_RXP_STATUS);
+       __raw_writew(cpu_to_be16((u16) maplen - sizeof(*rxp_hdr)),
+                    elem->hw_desc + C2_RXP_LEN);
+       __raw_writeq(cpu_to_be64(mapaddr), elem->hw_desc + C2_RXP_ADDR);
+       __raw_writew(cpu_to_be16(RXP_HRXD_READY), elem->hw_desc + C2_RXP_FLAGS);
+
+       elem->skb = skb;
+       elem->mapaddr = mapaddr;
+       elem->maplen = maplen;
+       rx_desc->len = maplen;
+
+       return 0;
+}
+
+/*
+ * Allocate buffers for the Rx ring
+ * For receive:  rx_ring.to_clean is next received frame
+ */
+static int c2_rx_fill(struct c2_port *c2_port)
+{
+       struct c2_ring *rx_ring = &c2_port->rx_ring;
+       struct c2_element *elem;
+       int ret = 0;
+
+       elem = rx_ring->start;
+       do {
+               if (c2_rx_alloc(c2_port, elem)) {
+                       ret = 1;
+                       break;
+               }
+       } while ((elem = elem->next) != rx_ring->start);
+
+       rx_ring->to_clean = rx_ring->start;
+       return ret;
+}
+
+/* Free all buffers in RX ring, assumes receiver stopped */
+static void c2_rx_clean(struct c2_port *c2_port)
+{
+       struct c2_dev *c2dev = c2_port->c2dev;
+       struct c2_ring *rx_ring = &c2_port->rx_ring;
+       struct c2_element *elem;
+       struct c2_rx_desc *rx_desc;
+
+       elem = rx_ring->start;
+       do {
+               rx_desc = elem->ht_desc;
+               rx_desc->len = 0;
+
+               __raw_writew(0, elem->hw_desc + C2_RXP_STATUS);
+               __raw_writew(0, elem->hw_desc + C2_RXP_COUNT);
+               __raw_writew(0, elem->hw_desc + C2_RXP_LEN);
+               __raw_writeq(cpu_to_be64(0x99aabbccddeeffULL),
+                            elem->hw_desc + C2_RXP_ADDR);
+               __raw_writew(cpu_to_be16(RXP_HRXD_UNINIT),
+                            elem->hw_desc + C2_RXP_FLAGS);
+
+               if (elem->skb) {
+                       pci_unmap_single(c2dev->pcidev, elem->mapaddr,
+                                        elem->maplen, PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(elem->skb);
+                       elem->skb = NULL;
+               }
+       } while ((elem = elem->next) != rx_ring->start);
+}
+
+static inline int c2_tx_free(struct c2_dev *c2dev, struct c2_element *elem)
+{
+       struct c2_tx_desc *tx_desc = elem->ht_desc;
+
+       tx_desc->len = 0;
+
+       pci_unmap_single(c2dev->pcidev, elem->mapaddr, elem->maplen,
+                        PCI_DMA_TODEVICE);
+
+       if (elem->skb) {
+               dev_kfree_skb_any(elem->skb);
+               elem->skb = NULL;
+       }
+
+       return 0;
+}
+
+/* Free all buffers in TX ring, assumes transmitter stopped */
+static void c2_tx_clean(struct c2_port *c2_port)
+{
+       struct c2_ring *tx_ring = &c2_port->tx_ring;
+       struct c2_element *elem;
+       struct c2_txp_desc txp_htxd;
+       int retry;
+       unsigned long flags;
+
+       spin_lock_irqsave(&c2_port->tx_lock, flags);
+
+       elem = tx_ring->start;
+
+       do {
+               retry = 0;
+               do {
+                       txp_htxd.flags =
+                           readw(elem->hw_desc + C2_TXP_FLAGS);
+
+                       if (txp_htxd.flags == TXP_HTXD_READY) {
+                               retry = 1;
+                               __raw_writew(0,
+                                            elem->hw_desc + C2_TXP_LEN);
+                               __raw_writeq(0,
+                                            elem->hw_desc + C2_TXP_ADDR);
+                               __raw_writew(cpu_to_be16(TXP_HTXD_DONE),
+                                            elem->hw_desc + C2_TXP_FLAGS);
+                               c2_port->netstats.tx_dropped++;
+                               break;
+                       } else {
+                               __raw_writew(0,
+                                            elem->hw_desc + C2_TXP_LEN);
+                               __raw_writeq(cpu_to_be64(0x1122334455667788ULL),
+                                            elem->hw_desc + C2_TXP_ADDR);
+                               __raw_writew(cpu_to_be16(TXP_HTXD_UNINIT),
+                                            elem->hw_desc + C2_TXP_FLAGS);
+                       }
+
+                       c2_tx_free(c2_port->c2dev, elem);
+
+               } while ((elem = elem->next) != tx_ring->start);
+       } while (retry);
+
+       c2_port->tx_avail = c2_port->tx_ring.count - 1;
+       c2_port->c2dev->cur_tx = tx_ring->to_use - tx_ring->start;
+
+       if (c2_port->tx_avail > MAX_SKB_FRAGS + 1)
+               netif_wake_queue(c2_port->netdev);
+
+       spin_unlock_irqrestore(&c2_port->tx_lock, flags);
+}
+
+/*
+ * Process transmit descriptors marked 'DONE' by the firmware,
+ * freeing up their unneeded sk_buffs.
+ */
+static void c2_tx_interrupt(struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+       struct c2_dev *c2dev = c2_port->c2dev;
+       struct c2_ring *tx_ring = &c2_port->tx_ring;
+       struct c2_element *elem;
+       struct c2_txp_desc txp_htxd;
+
+       spin_lock(&c2_port->tx_lock);
+
+       for (elem = tx_ring->to_clean; elem != tx_ring->to_use;
+            elem = elem->next) {
+               txp_htxd.flags =
+                   be16_to_cpu(readw(elem->hw_desc + C2_TXP_FLAGS));
+
+               if (txp_htxd.flags != TXP_HTXD_DONE)
+                       break;
+
+               if (netif_msg_tx_done(c2_port)) {
+                       /* PCI reads are expensive in fast path */
+                       txp_htxd.len =
+                           be16_to_cpu(readw(elem->hw_desc + C2_TXP_LEN));
+                       pr_debug("%s: tx done slot %3Zu status 0x%x len "
+                               "%5u bytes\n",
+                               netdev->name, elem - tx_ring->start,
+                               txp_htxd.flags, txp_htxd.len);
+               }
+
+               c2_tx_free(c2dev, elem);
+               ++(c2_port->tx_avail);
+       }
+
+       tx_ring->to_clean = elem;
+
+       if (netif_queue_stopped(netdev)
+           && c2_port->tx_avail > MAX_SKB_FRAGS + 1)
+               netif_wake_queue(netdev);
+
+       spin_unlock(&c2_port->tx_lock);
+}
+
+static void c2_rx_error(struct c2_port *c2_port, struct c2_element *elem)
+{
+       struct c2_rx_desc *rx_desc = elem->ht_desc;
+       struct c2_rxp_hdr *rxp_hdr = (struct c2_rxp_hdr *) elem->skb->data;
+
+       if (rxp_hdr->status != RXP_HRXD_OK ||
+           rxp_hdr->len > (rx_desc->len - sizeof(*rxp_hdr))) {
+               pr_debug("BAD RXP_HRXD\n");
+               pr_debug("  rx_desc : %p\n", rx_desc);
+               pr_debug("    index : %Zu\n",
+                       elem - c2_port->rx_ring.start);
+               pr_debug("    len   : %u\n", rx_desc->len);
+               pr_debug("  rxp_hdr : %p [PA %p]\n", rxp_hdr,
+                       (void *) __pa((unsigned long) rxp_hdr));
+               pr_debug("    flags : 0x%x\n", rxp_hdr->flags);
+               pr_debug("    status: 0x%x\n", rxp_hdr->status);
+               pr_debug("    len   : %u\n", rxp_hdr->len);
+               pr_debug("    rsvd  : 0x%x\n", rxp_hdr->rsvd);
+       }
+
+       /* Setup the skb for reuse since we're dropping this pkt */
+       elem->skb->tail = elem->skb->data = elem->skb->head;
+
+       /* Zero out the rxp hdr in the sk_buff */
+       memset(elem->skb->data, 0, sizeof(*rxp_hdr));
+
+       /* Write the descriptor to the adapter's rx ring */
+       __raw_writew(0, elem->hw_desc + C2_RXP_STATUS);
+       __raw_writew(0, elem->hw_desc + C2_RXP_COUNT);
+       __raw_writew(cpu_to_be16((u16) elem->maplen - sizeof(*rxp_hdr)),
+                    elem->hw_desc + C2_RXP_LEN);
+       __raw_writeq(cpu_to_be64(elem->mapaddr), elem->hw_desc + C2_RXP_ADDR);
+       __raw_writew(cpu_to_be16(RXP_HRXD_READY), elem->hw_desc + C2_RXP_FLAGS);
+
+       pr_debug("packet dropped\n");
+       c2_port->netstats.rx_dropped++;
+}
+
+static void c2_rx_interrupt(struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+       struct c2_dev *c2dev = c2_port->c2dev;
+       struct c2_ring *rx_ring = &c2_port->rx_ring;
+       struct c2_element *elem;
+       struct c2_rx_desc *rx_desc;
+       struct c2_rxp_hdr *rxp_hdr;
+       struct sk_buff *skb;
+       dma_addr_t mapaddr;
+       u32 maplen, buflen;
+       unsigned long flags;
+
+       spin_lock_irqsave(&c2dev->lock, flags);
+
+       /* Begin where we left off */
+       rx_ring->to_clean = rx_ring->start + c2dev->cur_rx;
+
+       for (elem = rx_ring->to_clean; elem->next != rx_ring->to_clean;
+            elem = elem->next) {
+               rx_desc = elem->ht_desc;
+               mapaddr = elem->mapaddr;
+               maplen = elem->maplen;
+               skb = elem->skb;
+               rxp_hdr = (struct c2_rxp_hdr *) skb->data;
+
+               if (rxp_hdr->flags != RXP_HRXD_DONE)
+                       break;
+               buflen = rxp_hdr->len;
+
+               /* Sanity check the RXP header */
+               if (rxp_hdr->status != RXP_HRXD_OK ||
+                   buflen > (rx_desc->len - sizeof(*rxp_hdr))) {
+                       c2_rx_error(c2_port, elem);
+                       continue;
+               }
+
+               /*
+                * Allocate and map a new skb for replenishing the host
+                * RX desc
+                */
+               if (c2_rx_alloc(c2_port, elem)) {
+                       c2_rx_error(c2_port, elem);
+                       continue;
+               }
+
+               /* Unmap the old skb */
+               pci_unmap_single(c2dev->pcidev, mapaddr, maplen,
+                                PCI_DMA_FROMDEVICE);
+
+               prefetch(skb->data);
+
+               /*
+                * Skip past the leading 8 bytes comprising of the
+                * "struct c2_rxp_hdr", prepended by the adapter
+                * to the usual Ethernet header ("struct ethhdr"),
+                * to the start of the raw Ethernet packet.
+                *
+                * Fix up the various fields in the sk_buff before
+                * passing it up to netif_rx(). The transfer size
+                * (in bytes) specified by the adapter len field of
+                * the "struct rxp_hdr_t" does NOT include the
+                * "sizeof(struct c2_rxp_hdr)".
+                */
+               skb->data += sizeof(*rxp_hdr);
+               skb->tail = skb->data + buflen;
+               skb->len = buflen;
+               skb->dev = netdev;
+               skb->protocol = eth_type_trans(skb, netdev);
+
+               netif_rx(skb);
+
+               netdev->last_rx = jiffies;
+               c2_port->netstats.rx_packets++;
+               c2_port->netstats.rx_bytes += buflen;
+       }
+
+       /* Save where we left off */
+       rx_ring->to_clean = elem;
+       c2dev->cur_rx = elem - rx_ring->start;
+       C2_SET_CUR_RX(c2dev, c2dev->cur_rx);
+
+       spin_unlock_irqrestore(&c2dev->lock, flags);
+}
+
+/*
+ * Handle netisr0 TX & RX interrupts.
+ */
+static irqreturn_t c2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned int netisr0, dmaisr;
+       int handled = 0;
+       struct c2_dev *c2dev = (struct c2_dev *) dev_id;
+
+       /* Process CCILNET interrupts */
+       netisr0 = readl(c2dev->regs + C2_NISR0);
+       if (netisr0) {
+
+               /*
+                * There is an issue with the firmware that always
+                * provides the status of RX for both TX & RX
+                * interrupts.  So process both queues here.
+                */
+               c2_rx_interrupt(c2dev->netdev);
+               c2_tx_interrupt(c2dev->netdev);
+
+               /* Clear the interrupt */
+               writel(netisr0, c2dev->regs + C2_NISR0);
+               handled++;
+       }
+
+       /* Process RNIC interrupts */
+       dmaisr = readl(c2dev->regs + C2_DISR);
+       if (dmaisr) {
+               writel(dmaisr, c2dev->regs + C2_DISR);
+               c2_rnic_interrupt(c2dev);
+               handled++;
+       }
+
+       if (handled) {
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
+}
+
+static int c2_up(struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+       struct c2_dev *c2dev = c2_port->c2dev;
+       struct c2_element *elem;
+       struct c2_rxp_hdr *rxp_hdr;
+       struct in_device *in_dev;
+       size_t rx_size, tx_size;
+       int ret, i;
+       unsigned int netimr0;
+
+       if (netif_msg_ifup(c2_port))
+               pr_debug("%s: enabling interface\n", netdev->name);
+
+       /* Set the Rx buffer size based on MTU */
+       c2_set_rxbufsize(c2_port);
+
+       /* Allocate DMA'able memory for Tx/Rx host descriptor rings */
+       rx_size = c2_port->rx_ring.count * sizeof(struct c2_rx_desc);
+       tx_size = c2_port->tx_ring.count * sizeof(struct c2_tx_desc);
+
+       c2_port->mem_size = tx_size + rx_size;
+       c2_port->mem = pci_alloc_consistent(c2dev->pcidev, c2_port->mem_size,
+                                           &c2_port->dma);
+       if (c2_port->mem == NULL) {
+               pr_debug("Unable to allocate memory for "
+                       "host descriptor rings\n");
+               return -ENOMEM;
+       }
+
+       memset(c2_port->mem, 0, c2_port->mem_size);
+
+       /* Create the Rx host descriptor ring */
+       if ((ret =
+            c2_rx_ring_alloc(&c2_port->rx_ring, c2_port->mem, c2_port->dma,
+                             c2dev->mmio_rxp_ring))) {
+               pr_debug("Unable to create RX ring\n");
+               goto bail0;
+       }
+
+       /* Allocate Rx buffers for the host descriptor ring */
+       if (c2_rx_fill(c2_port)) {
+               pr_debug("Unable to fill RX ring\n");
+               goto bail1;
+       }
+
+       /* Create the Tx host descriptor ring */
+       if ((ret = c2_tx_ring_alloc(&c2_port->tx_ring, c2_port->mem + rx_size,
+                                   c2_port->dma + rx_size,
+                                   c2dev->mmio_txp_ring))) {
+               pr_debug("Unable to create TX ring\n");
+               goto bail1;
+       }
+
+       /* Set the TX pointer to where we left off */
+       c2_port->tx_avail = c2_port->tx_ring.count - 1;
+       c2_port->tx_ring.to_use = c2_port->tx_ring.to_clean =
+           c2_port->tx_ring.start + c2dev->cur_tx;
+
+       /* missing: Initialize MAC */
+
+       BUG_ON(c2_port->tx_ring.to_use != c2_port->tx_ring.to_clean);
+
+       /* Reset the adapter, ensures the driver is in sync with the RXP */
+       c2_reset(c2_port);
+
+       /* Reset the READY bit in the sk_buff RXP headers & adapter HRXDQ */
+       for (i = 0, elem = c2_port->rx_ring.start; i < c2_port->rx_ring.count;
+            i++, elem++) {
+               rxp_hdr = (struct c2_rxp_hdr *) elem->skb->data;
+               rxp_hdr->flags = 0;
+               __raw_writew(cpu_to_be16(RXP_HRXD_READY),
+                            elem->hw_desc + C2_RXP_FLAGS);
+       }
+
+       /* Enable network packets */
+       netif_start_queue(netdev);
+
+       /* Enable IRQ */
+       writel(0, c2dev->regs + C2_IDIS);
+       netimr0 = readl(c2dev->regs + C2_NIMR0);
+       netimr0 &= ~(C2_PCI_HTX_INT | C2_PCI_HRX_INT);
+       writel(netimr0, c2dev->regs + C2_NIMR0);
+
+       /* Tell the stack to ignore arp requests for ipaddrs bound to
+        * other interfaces.  This is needed to prevent the host stack
+        * from responding to arp requests to the ipaddr bound on the
+        * rdma interface.
+        */
+       in_dev = in_dev_get(netdev);
+       in_dev->cnf.arp_ignore = 1;
+       in_dev_put(in_dev);
+
+       return 0;
+
+      bail1:
+       c2_rx_clean(c2_port);
+       kfree(c2_port->rx_ring.start);
+
+      bail0:
+       pci_free_consistent(c2dev->pcidev, c2_port->mem_size, c2_port->mem,
+                           c2_port->dma);
+
+       return ret;
+}
+
+static int c2_down(struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+       struct c2_dev *c2dev = c2_port->c2dev;
+
+       if (netif_msg_ifdown(c2_port))
+               pr_debug("%s: disabling interface\n",
+                       netdev->name);
+
+       /* Wait for all the queued packets to get sent */
+       c2_tx_interrupt(netdev);
+
+       /* Disable network packets */
+       netif_stop_queue(netdev);
+
+       /* Disable IRQs by clearing the interrupt mask */
+       writel(1, c2dev->regs + C2_IDIS);
+       writel(0, c2dev->regs + C2_NIMR0);
+
+       /* missing: Stop transmitter */
+
+       /* missing: Stop receiver */
+
+       /* Reset the adapter, ensures the driver is in sync with the RXP */
+       c2_reset(c2_port);
+
+       /* missing: Turn off LEDs here */
+
+       /* Free all buffers in the host descriptor rings */
+       c2_tx_clean(c2_port);
+       c2_rx_clean(c2_port);
+
+       /* Free the host descriptor rings */
+       kfree(c2_port->rx_ring.start);
+       kfree(c2_port->tx_ring.start);
+       pci_free_consistent(c2dev->pcidev, c2_port->mem_size, c2_port->mem,
+                           c2_port->dma);
+
+       return 0;
+}
+
+static void c2_reset(struct c2_port *c2_port)
+{
+       struct c2_dev *c2dev = c2_port->c2dev;
+       unsigned int cur_rx = c2dev->cur_rx;
+
+       /* Tell the hardware to quiesce */
+       C2_SET_CUR_RX(c2dev, cur_rx | C2_PCI_HRX_QUI);
+
+       /*
+        * The hardware will reset the C2_PCI_HRX_QUI bit once
+        * the RXP is quiesced.  Wait 2 seconds for this.
+        */
+       ssleep(2);
+
+       cur_rx = C2_GET_CUR_RX(c2dev);
+
+       if (cur_rx & C2_PCI_HRX_QUI)
+               pr_debug("c2_reset: failed to quiesce the hardware!\n");
+
+       cur_rx &= ~C2_PCI_HRX_QUI;
+
+       c2dev->cur_rx = cur_rx;
+
+       pr_debug("Current RX: %u\n", c2dev->cur_rx);
+}
+
+static int c2_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+       struct c2_dev *c2dev = c2_port->c2dev;
+       struct c2_ring *tx_ring = &c2_port->tx_ring;
+       struct c2_element *elem;
+       dma_addr_t mapaddr;
+       u32 maplen;
+       unsigned long flags;
+       unsigned int i;
+
+       spin_lock_irqsave(&c2_port->tx_lock, flags);
+
+       if (unlikely(c2_port->tx_avail < (skb_shinfo(skb)->nr_frags + 1))) {
+               netif_stop_queue(netdev);
+               spin_unlock_irqrestore(&c2_port->tx_lock, flags);
+
+               pr_debug("%s: Tx ring full when queue awake!\n",
+                       netdev->name);
+               return NETDEV_TX_BUSY;
+       }
+
+       maplen = skb_headlen(skb);
+       mapaddr =
+           pci_map_single(c2dev->pcidev, skb->data, maplen, PCI_DMA_TODEVICE);
+
+       elem = tx_ring->to_use;
+       elem->skb = skb;
+       elem->mapaddr = mapaddr;
+       elem->maplen = maplen;
+
+       /* Tell HW to xmit */
+       __raw_writeq(cpu_to_be64(mapaddr), elem->hw_desc + C2_TXP_ADDR);
+       __raw_writew(cpu_to_be16(maplen), elem->hw_desc + C2_TXP_LEN);
+       __raw_writew(cpu_to_be16(TXP_HTXD_READY), elem->hw_desc + C2_TXP_FLAGS);
+
+       c2_port->netstats.tx_packets++;
+       c2_port->netstats.tx_bytes += maplen;
+
+       /* Loop thru additional data fragments and queue them */
+       if (skb_shinfo(skb)->nr_frags) {
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+                       maplen = frag->size;
+                       mapaddr =
+                           pci_map_page(c2dev->pcidev, frag->page,
+                                        frag->page_offset, maplen,
+                                        PCI_DMA_TODEVICE);
+
+                       elem = elem->next;
+                       elem->skb = NULL;
+                       elem->mapaddr = mapaddr;
+                       elem->maplen = maplen;
+
+                       /* Tell HW to xmit */
+                       __raw_writeq(cpu_to_be64(mapaddr),
+                                    elem->hw_desc + C2_TXP_ADDR);
+                       __raw_writew(cpu_to_be16(maplen),
+                                    elem->hw_desc + C2_TXP_LEN);
+                       __raw_writew(cpu_to_be16(TXP_HTXD_READY),
+                                    elem->hw_desc + C2_TXP_FLAGS);
+
+                       c2_port->netstats.tx_packets++;
+                       c2_port->netstats.tx_bytes += maplen;
+               }
+       }
+
+       tx_ring->to_use = elem->next;
+       c2_port->tx_avail -= (skb_shinfo(skb)->nr_frags + 1);
+
+       if (c2_port->tx_avail <= MAX_SKB_FRAGS + 1) {
+               netif_stop_queue(netdev);
+               if (netif_msg_tx_queued(c2_port))
+                       pr_debug("%s: transmit queue full\n",
+                               netdev->name);
+       }
+
+       spin_unlock_irqrestore(&c2_port->tx_lock, flags);
+
+       netdev->trans_start = jiffies;
+
+       return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *c2_get_stats(struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+
+       return &c2_port->netstats;
+}
+
+static void c2_tx_timeout(struct net_device *netdev)
+{
+       struct c2_port *c2_port = netdev_priv(netdev);
+
+       if (netif_msg_timer(c2_port))
+               pr_debug("%s: tx timeout\n", netdev->name);
+
+       c2_tx_clean(c2_port);
+}
+
+static int c2_change_mtu(struct net_device *netdev, int new_mtu)
+{
+       int ret = 0;
+
+       if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
+               return -EINVAL;
+
+       netdev->mtu = new_mtu;
+
+       if (netif_running(netdev)) {
+               c2_down(netdev);
+
+               c2_up(netdev);
+       }
+
+       return ret;
+}
+
+/* Initialize network device */
+static struct net_device *c2_devinit(struct c2_dev *c2dev,
+                                    void __iomem * mmio_addr)
+{
+       struct c2_port *c2_port = NULL;
+       struct net_device *netdev = alloc_etherdev(sizeof(*c2_port));
+
+       if (!netdev) {
+               pr_debug("c2_port etherdev alloc failed");
+               return NULL;
+       }
+
+       SET_MODULE_OWNER(netdev);
+       SET_NETDEV_DEV(netdev, &c2dev->pcidev->dev);
+
+       netdev->open = c2_up;
+       netdev->stop = c2_down;
+       netdev->hard_start_xmit = c2_xmit_frame;
+       netdev->get_stats = c2_get_stats;
+       netdev->tx_timeout = c2_tx_timeout;
+       netdev->change_mtu = c2_change_mtu;
+       netdev->watchdog_timeo = C2_TX_TIMEOUT;
+       netdev->irq = c2dev->pcidev->irq;
+
+       c2_port = netdev_priv(netdev);
+       c2_port->netdev = netdev;
+       c2_port->c2dev = c2dev;
+       c2_port->msg_enable = netif_msg_init(debug, default_msg);
+       c2_port->tx_ring.count = C2_NUM_TX_DESC;
+       c2_port->rx_ring.count = C2_NUM_RX_DESC;
+
+       spin_lock_init(&c2_port->tx_lock);
+
+       /* Copy our 48-bit ethernet hardware address */
+       memcpy_fromio(netdev->dev_addr, mmio_addr + C2_REGS_ENADDR, 6);
+
+       /* Validate the MAC address */
+       if (!is_valid_ether_addr(netdev->dev_addr)) {
+               pr_debug("Invalid MAC Address\n");
+               c2_print_macaddr(netdev);
+               free_netdev(netdev);
+               return NULL;
+       }
+
+       c2dev->netdev = netdev;
+
+       return netdev;
+}
+
+static int __devinit c2_probe(struct pci_dev *pcidev,
+                             const struct pci_device_id *ent)
+{
+       int ret = 0, i;
+       unsigned long reg0_start, reg0_flags, reg0_len;
+       unsigned long reg2_start, reg2_flags, reg2_len;
+       unsigned long reg4_start, reg4_flags, reg4_len;
+       unsigned kva_map_size;
+       struct net_device *netdev = NULL;
+       struct c2_dev *c2dev = NULL;
+       void __iomem *mmio_regs = NULL;
+
+       printk(KERN_INFO PFX "AMSO1100 Gigabit Ethernet driver v%s loaded\n",
+               DRV_VERSION);
+
+       /* Enable PCI device */
+       ret = pci_enable_device(pcidev);
+       if (ret) {
+               printk(KERN_ERR PFX "%s: Unable to enable PCI device\n",
+                       pci_name(pcidev));
+               goto bail0;
+       }
+
+       reg0_start = pci_resource_start(pcidev, BAR_0);
+       reg0_len = pci_resource_len(pcidev, BAR_0);
+       reg0_flags = pci_resource_flags(pcidev, BAR_0);
+
+       reg2_start = pci_resource_start(pcidev, BAR_2);
+       reg2_len = pci_resource_len(pcidev, BAR_2);
+       reg2_flags = pci_resource_flags(pcidev, BAR_2);
+
+       reg4_start = pci_resource_start(pcidev, BAR_4);
+       reg4_len = pci_resource_len(pcidev, BAR_4);
+       reg4_flags = pci_resource_flags(pcidev, BAR_4);
+
+       pr_debug("BAR0 size = 0x%lX bytes\n", reg0_len);
+       pr_debug("BAR2 size = 0x%lX bytes\n", reg2_len);
+       pr_debug("BAR4 size = 0x%lX bytes\n", reg4_len);
+
+       /* Make sure PCI base addr are MMIO */
+       if (!(reg0_flags & IORESOURCE_MEM) ||
+           !(reg2_flags & IORESOURCE_MEM) || !(reg4_flags & IORESOURCE_MEM)) {
+               printk(KERN_ERR PFX "PCI regions not an MMIO resource\n");
+               ret = -ENODEV;
+               goto bail1;
+       }
+
+       /* Check for weird/broken PCI region reporting */
+       if ((reg0_len < C2_REG0_SIZE) ||
+           (reg2_len < C2_REG2_SIZE) || (reg4_len < C2_REG4_SIZE)) {
+               printk(KERN_ERR PFX "Invalid PCI region sizes\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 "%s: Unable to request regions\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 configuration failed\n");
+                       goto bail2;
+               }
+       } else {
+               ret = pci_set_dma_mask(pcidev, DMA_32BIT_MASK);
+               if (ret < 0) {
+                       printk(KERN_ERR PFX "32b DMA configuration failed\n");
+                       goto bail2;
+               }
+       }
+
+       /* Enables bus-mastering on the device */
+       pci_set_master(pcidev);
+
+       /* Remap the adapter PCI registers in BAR4 */
+       mmio_regs = ioremap_nocache(reg4_start + C2_PCI_REGS_OFFSET,
+                                   sizeof(struct c2_adapter_pci_regs));
+       if (mmio_regs == 0UL) {
+               printk(KERN_ERR PFX
+                       "Unable to remap adapter PCI registers in BAR4\n");
+               ret = -EIO;
+               goto bail2;
+       }
+
+       /* Validate PCI regs magic */
+       for (i = 0; i < sizeof(c2_magic); i++) {
+               if (c2_magic[i] != readb(mmio_regs + C2_REGS_MAGIC + i)) {
+                       printk(KERN_ERR PFX "Downlevel Firmware boot loader "
+                               "[%d/%Zd: got 0x%x, exp 0x%x]. Use the cc_flash "
+                              "utility to update your boot loader\n",
+                               i + 1, sizeof(c2_magic),
+                               readb(mmio_regs + C2_REGS_MAGIC + i),
+                               c2_magic[i]);
+                       printk(KERN_ERR PFX "Adapter not claimed\n");
+                       iounmap(mmio_regs);
+                       ret = -EIO;
+                       goto bail2;
+               }
+       }
+
+       /* Validate the adapter version */
+       if (be32_to_cpu(readl(mmio_regs + C2_REGS_VERS)) != C2_VERSION) {
+               printk(KERN_ERR PFX "Version mismatch "
+                       "[fw=%u, c2=%u], Adapter not claimed\n",
+                       be32_to_cpu(readl(mmio_regs + C2_REGS_VERS)),
+                       C2_VERSION);
+               ret = -EINVAL;
+               iounmap(mmio_regs);
+               goto bail2;
+       }
+
+       /* Validate the adapter IVN */
+       if (be32_to_cpu(readl(mmio_regs + C2_REGS_IVN)) != C2_IVN) {
+               printk(KERN_ERR PFX "Downlevel FIrmware level. You should be using "
+                      "the OpenIB device support kit. "
+                      "[fw=0x%x, c2=0x%x], Adapter not claimed\n",
+                       be32_to_cpu(readl(mmio_regs + C2_REGS_IVN)),
+                       C2_IVN);
+               ret = -EINVAL;
+               iounmap(mmio_regs);
+               goto bail2;
+       }
+
+       /* Allocate hardware structure */
+       c2dev = (struct c2_dev *) ib_alloc_device(sizeof(*c2dev));
+       if (!c2dev) {
+               printk(KERN_ERR PFX "%s: Unable to alloc hardware struct\n",
+                       pci_name(pcidev));
+               ret = -ENOMEM;
+               iounmap(mmio_regs);
+               goto bail2;
+       }
+
+       memset(c2dev, 0, sizeof(*c2dev));
+       spin_lock_init(&c2dev->lock);
+       c2dev->pcidev = pcidev;
+       c2dev->cur_tx = 0;
+
+       /* Get the last RX index */
+       c2dev->cur_rx =
+           (be32_to_cpu(readl(mmio_regs + C2_REGS_HRX_CUR)) -
+            0xffffc000) / sizeof(struct c2_rxp_desc);
+
+       /* Request an interrupt line for the driver */
+       ret = request_irq(pcidev->irq, c2_interrupt, SA_SHIRQ, DRV_NAME, c2dev);
+       if (ret) {
+               printk(KERN_ERR PFX "%s: requested IRQ %u is busy\n",
+                       pci_name(pcidev), pcidev->irq);
+               iounmap(mmio_regs);
+               goto bail3;
+       }
+
+       /* Set driver specific data */
+       pci_set_drvdata(pcidev, c2dev);
+
+       /* Initialize network device */
+       if ((netdev = c2_devinit(c2dev, mmio_regs)) == NULL) {
+               iounmap(mmio_regs);
+               goto bail4;
+       }
+
+       /* Save off the actual size prior to unmapping mmio_regs */
+       kva_map_size = be32_to_cpu(readl(mmio_regs + C2_REGS_PCI_WINSIZE));
+
+       /* Unmap the adapter PCI registers in BAR4 */
+       iounmap(mmio_regs);
+
+       /* Register network device */
+       ret = register_netdev(netdev);
+       if (ret) {
+               printk(KERN_ERR PFX "Unable to register netdev, ret = %d\n",
+                       ret);
+               goto bail5;
+       }
+
+       /* Disable network packets */
+       netif_stop_queue(netdev);
+
+       /* Remap the adapter HRXDQ PA space to kernel VA space */
+       c2dev->mmio_rxp_ring = ioremap_nocache(reg4_start + C2_RXP_HRXDQ_OFFSET,
+                                              C2_RXP_HRXDQ_SIZE);
+       if (c2dev->mmio_rxp_ring == 0UL) {
+               printk(KERN_ERR PFX "Unable to remap MMIO HRXDQ region\n");
+               ret = -EIO;
+               goto bail6;
+       }
+
+       /* Remap the adapter HTXDQ PA space to kernel VA space */
+       c2dev->mmio_txp_ring = ioremap_nocache(reg4_start + C2_TXP_HTXDQ_OFFSET,
+                                              C2_TXP_HTXDQ_SIZE);
+       if (c2dev->mmio_txp_ring == 0UL) {
+               printk(KERN_ERR PFX "Unable to remap MMIO HTXDQ region\n");
+               ret = -EIO;
+               goto bail7;
+       }
+
+       /* Save off the current RX index in the last 4 bytes of the TXP Ring */
+       C2_SET_CUR_RX(c2dev, c2dev->cur_rx);
+
+       /* Remap the PCI registers in adapter BAR0 to kernel VA space */
+       c2dev->regs = ioremap_nocache(reg0_start, reg0_len);
+       if (c2dev->regs == 0UL) {
+               printk(KERN_ERR PFX "Unable to remap BAR0\n");
+               ret = -EIO;
+               goto bail8;
+       }
+
+       /* Remap the PCI registers in adapter BAR4 to kernel VA space */
+       c2dev->pa = reg4_start + C2_PCI_REGS_OFFSET;
+       c2dev->kva = ioremap_nocache(reg4_start + C2_PCI_REGS_OFFSET,
+                                    kva_map_size);
+       if (c2dev->kva == 0UL) {
+               printk(KERN_ERR PFX "Unable to remap BAR4\n");
+               ret = -EIO;
+               goto bail9;
+       }
+
+       /* Print out the MAC address */
+       c2_print_macaddr(netdev);
+
+       ret = c2_rnic_init(c2dev);
+       if (ret) {
+               printk(KERN_ERR PFX "c2_rnic_init failed: %d\n", ret);
+               goto bail10;
+       }
+
+       c2_register_device(c2dev);
+
+       return 0;
+
+ bail10:
+       iounmap(c2dev->kva);
+
+ bail9:
+       iounmap(c2dev->regs);
+
+ bail8:
+       iounmap(c2dev->mmio_txp_ring);
+
+ bail7:
+       iounmap(c2dev->mmio_rxp_ring);
+
+ bail6:
+       unregister_netdev(netdev);
+
+ bail5:
+       free_netdev(netdev);
+
+ bail4:
+       free_irq(pcidev->irq, c2dev);
+
+ bail3:
+       ib_dealloc_device(&c2dev->ibdev);
+
+ bail2:
+       pci_release_regions(pcidev);
+
+ bail1:
+       pci_disable_device(pcidev);
+
+ bail0:
+       return ret;
+}
+
+static void __devexit c2_remove(struct pci_dev *pcidev)
+{
+       struct c2_dev *c2dev = pci_get_drvdata(pcidev);
+       struct net_device *netdev = c2dev->netdev;
+
+       /* Unregister with OpenIB */
+       c2_unregister_device(c2dev);
+
+       /* Clean up the RNIC resources */
+       c2_rnic_term(c2dev);
+
+       /* Remove network device from the kernel */
+       unregister_netdev(netdev);
+
+       /* Free network device */
+       free_netdev(netdev);
+
+       /* Free the interrupt line */
+       free_irq(pcidev->irq, c2dev);
+
+       /* missing: Turn LEDs off here */
+
+       /* Unmap adapter PA space */
+       iounmap(c2dev->kva);
+       iounmap(c2dev->regs);
+       iounmap(c2dev->mmio_txp_ring);
+       iounmap(c2dev->mmio_rxp_ring);
+
+       /* Free the hardware structure */
+       ib_dealloc_device(&c2dev->ibdev);
+
+       /* Release reserved PCI I/O and memory resources */
+       pci_release_regions(pcidev);
+
+       /* Disable PCI device */
+       pci_disable_device(pcidev);
+
+       /* Clear driver specific data */
+       pci_set_drvdata(pcidev, NULL);
+}
+
+static struct pci_driver c2_pci_driver = {
+       .name = DRV_NAME,
+       .id_table = c2_pci_table,
+       .probe = c2_probe,
+       .remove = __devexit_p(c2_remove),
+};
+
+static int __init c2_init_module(void)
+{
+       return pci_module_init(&c2_pci_driver);
+}
+
+static void __exit c2_exit_module(void)
+{
+       pci_unregister_driver(&c2_pci_driver);
+}
+
+module_init(c2_init_module);
+module_exit(c2_exit_module);
diff --git a/drivers/infiniband/hw/amso1100/c2.h b/drivers/infiniband/hw/amso1100/c2.h
new file mode 100644 (file)
index 0000000..1b17dcd
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 __C2_H
+#define __C2_H
+
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/idr.h>
+#include <asm/semaphore.h>
+
+#include "c2_provider.h"
+#include "c2_mq.h"
+#include "c2_status.h"
+
+#define DRV_NAME     "c2"
+#define DRV_VERSION  "1.1"
+#define PFX          DRV_NAME ": "
+
+#define BAR_0                0
+#define BAR_2                2
+#define BAR_4                4
+
+#define RX_BUF_SIZE         (1536 + 8)
+#define ETH_JUMBO_MTU        9000
+#define C2_MAGIC            "CEPHEUS"
+#define C2_VERSION           4
+#define C2_IVN              (18 & 0x7fffffff)
+
+#define C2_REG0_SIZE        (16 * 1024)
+#define C2_REG2_SIZE        (2 * 1024 * 1024)
+#define C2_REG4_SIZE        (256 * 1024 * 1024)
+#define C2_NUM_TX_DESC       341
+#define C2_NUM_RX_DESC       256
+#define C2_PCI_REGS_OFFSET  (0x10000)
+#define C2_RXP_HRXDQ_OFFSET (((C2_REG4_SIZE)/2))
+#define C2_RXP_HRXDQ_SIZE   (4096)
+#define C2_TXP_HTXDQ_OFFSET (((C2_REG4_SIZE)/2) + C2_RXP_HRXDQ_SIZE)
+#define C2_TXP_HTXDQ_SIZE   (4096)
+#define C2_TX_TIMEOUT      (6*HZ)
+
+/* CEPHEUS */
+static const u8 c2_magic[] = {
+       0x43, 0x45, 0x50, 0x48, 0x45, 0x55, 0x53
+};
+
+enum adapter_pci_regs {
+       C2_REGS_MAGIC = 0x0000,
+       C2_REGS_VERS = 0x0008,
+       C2_REGS_IVN = 0x000C,
+       C2_REGS_PCI_WINSIZE = 0x0010,
+       C2_REGS_Q0_QSIZE = 0x0014,
+       C2_REGS_Q0_MSGSIZE = 0x0018,
+       C2_REGS_Q0_POOLSTART = 0x001C,
+       C2_REGS_Q0_SHARED = 0x0020,
+       C2_REGS_Q1_QSIZE = 0x0024,
+       C2_REGS_Q1_MSGSIZE = 0x0028,
+       C2_REGS_Q1_SHARED = 0x0030,
+       C2_REGS_Q2_QSIZE = 0x0034,
+       C2_REGS_Q2_MSGSIZE = 0x0038,
+       C2_REGS_Q2_SHARED = 0x0040,
+       C2_REGS_ENADDR = 0x004C,
+       C2_REGS_RDMA_ENADDR = 0x0054,
+       C2_REGS_HRX_CUR = 0x006C,
+};
+
+struct c2_adapter_pci_regs {
+       char reg_magic[8];
+       u32 version;
+       u32 ivn;
+       u32 pci_window_size;
+       u32 q0_q_size;
+       u32 q0_msg_size;
+       u32 q0_pool_start;
+       u32 q0_shared;
+       u32 q1_q_size;
+       u32 q1_msg_size;
+       u32 q1_pool_start;
+       u32 q1_shared;
+       u32 q2_q_size;
+       u32 q2_msg_size;
+       u32 q2_pool_start;
+       u32 q2_shared;
+       u32 log_start;
+       u32 log_size;
+       u8 host_enaddr[8];
+       u8 rdma_enaddr[8];
+       u32 crash_entry;
+       u32 crash_ready[2];
+       u32 fw_txd_cur;
+       u32 fw_hrxd_cur;
+       u32 fw_rxd_cur;
+};
+
+enum pci_regs {
+       C2_HISR = 0x0000,
+       C2_DISR = 0x0004,
+       C2_HIMR = 0x0008,
+       C2_DIMR = 0x000C,
+       C2_NISR0 = 0x0010,
+       C2_NISR1 = 0x0014,
+       C2_NIMR0 = 0x0018,
+       C2_NIMR1 = 0x001C,
+       C2_IDIS = 0x0020,
+};
+
+enum {
+       C2_PCI_HRX_INT = 1 << 8,
+       C2_PCI_HTX_INT = 1 << 17,
+       C2_PCI_HRX_QUI = 1 << 31,
+};
+
+/*
+ * Cepheus registers in BAR0.
+ */
+struct c2_pci_regs {
+       u32 hostisr;
+       u32 dmaisr;
+       u32 hostimr;
+       u32 dmaimr;
+       u32 netisr0;
+       u32 netisr1;
+       u32 netimr0;
+       u32 netimr1;
+       u32 int_disable;
+};
+
+/* TXP flags */
+enum c2_txp_flags {
+       TXP_HTXD_DONE = 0,
+       TXP_HTXD_READY = 1 << 0,
+       TXP_HTXD_UNINIT = 1 << 1,
+};
+
+/* RXP flags */
+enum c2_rxp_flags {
+       RXP_HRXD_UNINIT = 0,
+       RXP_HRXD_READY = 1 << 0,
+       RXP_HRXD_DONE = 1 << 1,
+};
+
+/* RXP status */
+enum c2_rxp_status {
+       RXP_HRXD_ZERO = 0,
+       RXP_HRXD_OK = 1 << 0,
+       RXP_HRXD_BUF_OV = 1 << 1,
+};
+
+/* TXP descriptor fields */
+enum txp_desc {
+       C2_TXP_FLAGS = 0x0000,
+       C2_TXP_LEN = 0x0002,
+       C2_TXP_ADDR = 0x0004,
+};
+
+/* RXP descriptor fields */
+enum rxp_desc {
+       C2_RXP_FLAGS = 0x0000,
+       C2_RXP_STATUS = 0x0002,
+       C2_RXP_COUNT = 0x0004,
+       C2_RXP_LEN = 0x0006,
+       C2_RXP_ADDR = 0x0008,
+};
+
+struct c2_txp_desc {
+       u16 flags;
+       u16 len;
+       u64 addr;
+} __attribute__ ((packed));
+
+struct c2_rxp_desc {
+       u16 flags;
+       u16 status;
+       u16 count;
+       u16 len;
+       u64 addr;
+} __attribute__ ((packed));
+
+struct c2_rxp_hdr {
+       u16 flags;
+       u16 status;
+       u16 len;
+       u16 rsvd;
+} __attribute__ ((packed));
+
+struct c2_tx_desc {
+       u32 len;
+       u32 status;
+       dma_addr_t next_offset;
+};
+
+struct c2_rx_desc {
+       u32 len;
+       u32 status;
+       dma_addr_t next_offset;
+};
+
+struct c2_alloc {
+       u32 last;
+       u32 max;
+       spinlock_t lock;
+       unsigned long *table;
+};
+
+struct c2_array {
+       struct {
+               void **page;
+               int used;
+       } *page_list;
+};
+
+/*
+ * The MQ shared pointer pool is organized as a linked list of
+ * chunks. Each chunk contains a linked list of free shared pointers
+ * that can be allocated to a given user mode client.
+ *
+ */
+struct sp_chunk {
+       struct sp_chunk *next;
+       dma_addr_t dma_addr;
+       DECLARE_PCI_UNMAP_ADDR(mapping);
+       u16 head;
+       u16 shared_ptr[0];
+};
+
+struct c2_pd_table {
+       u32 last;
+       u32 max;
+       spinlock_t lock;
+       unsigned long *table;
+};
+
+struct c2_qp_table {
+       struct idr idr;
+       spinlock_t lock;
+       int last;
+};
+
+struct c2_element {
+       struct c2_element *next;
+       void *ht_desc;          /* host     descriptor */
+       void __iomem *hw_desc;  /* hardware descriptor */
+       struct sk_buff *skb;
+       dma_addr_t mapaddr;
+       u32 maplen;
+};
+
+struct c2_ring {
+       struct c2_element *to_clean;
+       struct c2_element *to_use;
+       struct c2_element *start;
+       unsigned long count;
+};
+
+struct c2_dev {
+       struct ib_device ibdev;
+       void __iomem *regs;
+       void __iomem *mmio_txp_ring; /* remapped adapter memory for hw rings */
+       void __iomem *mmio_rxp_ring;
+       spinlock_t lock;
+       struct pci_dev *pcidev;
+       struct net_device *netdev;
+       struct net_device *pseudo_netdev;
+       unsigned int cur_tx;
+       unsigned int cur_rx;
+       u32 adapter_handle;
+       int device_cap_flags;
+       void __iomem *kva;      /* KVA device memory */
+       unsigned long pa;       /* PA device memory */
+       void **qptr_array;
+
+       kmem_cache_t *host_msg_cache;
+
+       struct list_head cca_link;              /* adapter list */
+       struct list_head eh_wakeup_list;        /* event wakeup list */
+       wait_queue_head_t req_vq_wo;
+
+       /* Cached RNIC properties */
+       struct ib_device_attr props;
+
+       struct c2_pd_table pd_table;
+       struct c2_qp_table qp_table;
+       int ports;              /* num of GigE ports */
+       int devnum;
+       spinlock_t vqlock;      /* sync vbs req MQ */
+
+       /* Verbs Queues */
+       struct c2_mq req_vq;    /* Verbs Request MQ */
+       struct c2_mq rep_vq;    /* Verbs Reply MQ */
+       struct c2_mq aeq;       /* Async Events MQ */
+
+       /* Kernel client MQs */
+       struct sp_chunk *kern_mqsp_pool;
+
+       /* Device updates these values when posting messages to a host
+        * target queue */
+       u16 req_vq_shared;
+       u16 rep_vq_shared;
+       u16 aeq_shared;
+       u16 irq_claimed;
+
+       /*
+        * Shared host target pages for user-accessible MQs.
+        */
+       int hthead;             /* index of first free entry */
+       void *htpages;          /* kernel vaddr */
+       int htlen;              /* length of htpages memory */
+       void *htuva;            /* user mapped vaddr */
+       spinlock_t htlock;      /* serialize allocation */
+
+       u64 adapter_hint_uva;   /* access to the activity FIFO */
+
+       //      spinlock_t aeq_lock;
+       //      spinlock_t rnic_lock;
+
+       u16 *hint_count;
+       dma_addr_t hint_count_dma;
+       u16 hints_read;
+
+       int init;               /* TRUE if it's ready */
+       char ae_cache_name[16];
+       char vq_cache_name[16];
+};
+
+struct c2_port {
+       u32 msg_enable;
+       struct c2_dev *c2dev;
+       struct net_device *netdev;
+
+       spinlock_t tx_lock;
+       u32 tx_avail;
+       struct c2_ring tx_ring;
+       struct c2_ring rx_ring;
+
+       void *mem;              /* PCI memory for host rings */
+       dma_addr_t dma;
+       unsigned long mem_size;
+
+       u32 rx_buf_size;
+
+       struct net_device_stats netstats;
+};
+
+/*
+ * Activity FIFO registers in BAR0.
+ */
+#define PCI_BAR0_HOST_HINT     0x100
+#define PCI_BAR0_ADAPTER_HINT  0x2000
+
+/*
+ * Ammasso PCI vendor id and Cepheus PCI device id.
+ */
+#define CQ_ARMED       0x01
+#define CQ_WAIT_FOR_DMA        0x80
+
+/*
+ * The format of a hint is as follows:
+ * Lower 16 bits are the count of hints for the queue.
+ * Next 15 bits are the qp_index
+ * Upper most bit depends on who reads it:
+ *    If read by producer, then it means Full (1) or Not-Full (0)
+ *    If read by consumer, then it means Empty (1) or Not-Empty (0)
+ */
+#define C2_HINT_MAKE(q_index, hint_count) (((q_index) << 16) | hint_count)
+#define C2_HINT_GET_INDEX(hint) (((hint) & 0x7FFF0000) >> 16)
+#define C2_HINT_GET_COUNT(hint) ((hint) & 0x0000FFFF)
+
+
+/*
+ * The following defines the offset in SDRAM for the c2_adapter_pci_regs_t
+ * struct.
+ */
+#define C2_ADAPTER_PCI_REGS_OFFSET 0x10000
+
+#ifndef readq
+static inline u64 readq(const void __iomem * addr)
+{
+       u64 ret = readl(addr + 4);
+       ret <<= 32;
+       ret |= readl(addr);
+
+       return ret;
+}
+#endif
+
+#ifndef writeq
+static inline void __raw_writeq(u64 val, void __iomem * addr)
+{
+       __raw_writel((u32) (val), addr);
+       __raw_writel((u32) (val >> 32), (addr + 4));
+}
+#endif
+
+#define C2_SET_CUR_RX(c2dev, cur_rx) \
+       __raw_writel(cpu_to_be32(cur_rx), c2dev->mmio_txp_ring + 4092)
+
+#define C2_GET_CUR_RX(c2dev) \
+       be32_to_cpu(readl(c2dev->mmio_txp_ring + 4092))
+
+static inline struct c2_dev *to_c2dev(struct ib_device *ibdev)
+{
+       return container_of(ibdev, struct c2_dev, ibdev);
+}
+
+static inline int c2_errno(void *reply)
+{
+       switch (c2_wr_get_result(reply)) {
+       case C2_OK:
+               return 0;
+       case CCERR_NO_BUFS:
+       case CCERR_INSUFFICIENT_RESOURCES:
+       case CCERR_ZERO_RDMA_READ_RESOURCES:
+               return -ENOMEM;
+       case CCERR_MR_IN_USE:
+       case CCERR_QP_IN_USE:
+               return -EBUSY;
+       case CCERR_ADDR_IN_USE:
+               return -EADDRINUSE;
+       case CCERR_ADDR_NOT_AVAIL:
+               return -EADDRNOTAVAIL;
+       case CCERR_CONN_RESET:
+               return -ECONNRESET;
+       case CCERR_NOT_IMPLEMENTED:
+       case CCERR_INVALID_WQE:
+               return -ENOSYS;
+       case CCERR_QP_NOT_PRIVILEGED:
+               return -EPERM;
+       case CCERR_STACK_ERROR:
+               return -EPROTO;
+       case CCERR_ACCESS_VIOLATION:
+       case CCERR_BASE_AND_BOUNDS_VIOLATION:
+               return -EFAULT;
+       case CCERR_STAG_STATE_NOT_INVALID:
+       case CCERR_INVALID_ADDRESS:
+       case CCERR_INVALID_CQ:
+       case CCERR_INVALID_EP:
+       case CCERR_INVALID_MODIFIER:
+       case CCERR_INVALID_MTU:
+       case CCERR_INVALID_PD_ID:
+       case CCERR_INVALID_QP:
+       case CCERR_INVALID_RNIC:
+       case CCERR_INVALID_STAG:
+               return -EINVAL;
+       default:
+               return -EAGAIN;
+       }
+}
+
+/* Device */
+extern int c2_register_device(struct c2_dev *c2dev);
+extern void c2_unregister_device(struct c2_dev *c2dev);
+extern int c2_rnic_init(struct c2_dev *c2dev);
+extern void c2_rnic_term(struct c2_dev *c2dev);
+extern void c2_rnic_interrupt(struct c2_dev *c2dev);
+extern int c2_del_addr(struct c2_dev *c2dev, u32 inaddr, u32 inmask);
+extern int c2_add_addr(struct c2_dev *c2dev, u32 inaddr, u32 inmask);
+
+/* QPs */
+extern int c2_alloc_qp(struct c2_dev *c2dev, struct c2_pd *pd,
+                      struct ib_qp_init_attr *qp_attrs, struct c2_qp *qp);
+extern void c2_free_qp(struct c2_dev *c2dev, struct c2_qp *qp);
+extern struct ib_qp *c2_get_qp(struct ib_device *device, int qpn);
+extern int c2_qp_modify(struct c2_dev *c2dev, struct c2_qp *qp,
+                       struct ib_qp_attr *attr, int attr_mask);
+extern int c2_qp_set_read_limits(struct c2_dev *c2dev, struct c2_qp *qp,
+                                int ord, int ird);
+extern int c2_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
+                       struct ib_send_wr **bad_wr);
+extern int c2_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr,
+                          struct ib_recv_wr **bad_wr);
+extern void __devinit c2_init_qp_table(struct c2_dev *c2dev);
+extern void __devexit c2_cleanup_qp_table(struct c2_dev *c2dev);
+extern void c2_set_qp_state(struct c2_qp *, int);
+extern struct c2_qp *c2_find_qpn(struct c2_dev *c2dev, int qpn);
+
+/* PDs */
+extern int c2_pd_alloc(struct c2_dev *c2dev, int privileged, struct c2_pd *pd);
+extern void c2_pd_free(struct c2_dev *c2dev, struct c2_pd *pd);
+extern int __devinit c2_init_pd_table(struct c2_dev *c2dev);
+extern void __devexit c2_cleanup_pd_table(struct c2_dev *c2dev);
+
+/* CQs */
+extern int c2_init_cq(struct c2_dev *c2dev, int entries,
+                     struct c2_ucontext *ctx, struct c2_cq *cq);
+extern void c2_free_cq(struct c2_dev *c2dev, struct c2_cq *cq);
+extern void c2_cq_event(struct c2_dev *c2dev, u32 mq_index);
+extern void c2_cq_clean(struct c2_dev *c2dev, struct c2_qp *qp, u32 mq_index);
+extern int c2_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
+extern int c2_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify notify);
+
+/* CM */
+extern int c2_llp_connect(struct iw_cm_id *cm_id,
+                         struct iw_cm_conn_param *iw_param);
+extern int c2_llp_accept(struct iw_cm_id *cm_id,
+                        struct iw_cm_conn_param *iw_param);
+extern int c2_llp_reject(struct iw_cm_id *cm_id, const void *pdata,
+                        u8 pdata_len);
+extern int c2_llp_service_create(struct iw_cm_id *cm_id, int backlog);
+extern int c2_llp_service_destroy(struct iw_cm_id *cm_id);
+
+/* MM */
+extern int c2_nsmr_register_phys_kern(struct c2_dev *c2dev, u64 *addr_list,
+                                     int page_size, int pbl_depth, u32 length,
+                                     u32 off, u64 *va, enum c2_acf acf,
+                                     struct c2_mr *mr);
+extern int c2_stag_dealloc(struct c2_dev *c2dev, u32 stag_index);
+
+/* AE */
+extern void c2_ae_event(struct c2_dev *c2dev, u32 mq_index);
+
+/* MQSP Allocator */
+extern int c2_init_mqsp_pool(struct c2_dev *c2dev, gfp_t gfp_mask,
+                            struct sp_chunk **root);
+extern void c2_free_mqsp_pool(struct c2_dev *c2dev, struct sp_chunk *root);
+extern u16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head,
+                         dma_addr_t *dma_addr, gfp_t gfp_mask);
+extern void c2_free_mqsp(u16 * mqsp);
+#endif
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c
new file mode 100644 (file)
index 0000000..08f46c8
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 "c2.h"
+#include <rdma/iw_cm.h>
+#include "c2_status.h"
+#include "c2_ae.h"
+
+static int c2_convert_cm_status(u32 c2_status)
+{
+       switch (c2_status) {
+       case C2_CONN_STATUS_SUCCESS:
+               return 0;
+       case C2_CONN_STATUS_REJECTED:
+               return -ENETRESET;
+       case C2_CONN_STATUS_REFUSED:
+               return -ECONNREFUSED;
+       case C2_CONN_STATUS_TIMEDOUT:
+               return -ETIMEDOUT;
+       case C2_CONN_STATUS_NETUNREACH:
+               return -ENETUNREACH;
+       case C2_CONN_STATUS_HOSTUNREACH:
+               return -EHOSTUNREACH;
+       case C2_CONN_STATUS_INVALID_RNIC:
+               return -EINVAL;
+       case C2_CONN_STATUS_INVALID_QP:
+               return -EINVAL;
+       case C2_CONN_STATUS_INVALID_QP_STATE:
+               return -EINVAL;
+       case C2_CONN_STATUS_ADDR_NOT_AVAIL:
+               return -EADDRNOTAVAIL;
+       default:
+               printk(KERN_ERR PFX
+                      "%s - Unable to convert CM status: %d\n",
+                      __FUNCTION__, c2_status);
+               return -EIO;
+       }
+}
+
+#ifdef DEBUG
+static const char* to_event_str(int event)
+{
+       static const char* event_str[] = {
+               "CCAE_REMOTE_SHUTDOWN",
+               "CCAE_ACTIVE_CONNECT_RESULTS",
+               "CCAE_CONNECTION_REQUEST",
+               "CCAE_LLP_CLOSE_COMPLETE",
+               "CCAE_TERMINATE_MESSAGE_RECEIVED",
+               "CCAE_LLP_CONNECTION_RESET",
+               "CCAE_LLP_CONNECTION_LOST",
+               "CCAE_LLP_SEGMENT_SIZE_INVALID",
+               "CCAE_LLP_INVALID_CRC",
+               "CCAE_LLP_BAD_FPDU",
+               "CCAE_INVALID_DDP_VERSION",
+               "CCAE_INVALID_RDMA_VERSION",
+               "CCAE_UNEXPECTED_OPCODE",
+               "CCAE_INVALID_DDP_QUEUE_NUMBER",
+               "CCAE_RDMA_READ_NOT_ENABLED",
+               "CCAE_RDMA_WRITE_NOT_ENABLED",
+               "CCAE_RDMA_READ_TOO_SMALL",
+               "CCAE_NO_L_BIT",
+               "CCAE_TAGGED_INVALID_STAG",
+               "CCAE_TAGGED_BASE_BOUNDS_VIOLATION",
+               "CCAE_TAGGED_ACCESS_RIGHTS_VIOLATION",
+               "CCAE_TAGGED_INVALID_PD",
+               "CCAE_WRAP_ERROR",
+               "CCAE_BAD_CLOSE",
+               "CCAE_BAD_LLP_CLOSE",
+               "CCAE_INVALID_MSN_RANGE",
+               "CCAE_INVALID_MSN_GAP",
+               "CCAE_IRRQ_OVERFLOW",
+               "CCAE_IRRQ_MSN_GAP",
+               "CCAE_IRRQ_MSN_RANGE",
+               "CCAE_IRRQ_INVALID_STAG",
+               "CCAE_IRRQ_BASE_BOUNDS_VIOLATION",
+               "CCAE_IRRQ_ACCESS_RIGHTS_VIOLATION",
+               "CCAE_IRRQ_INVALID_PD",
+               "CCAE_IRRQ_WRAP_ERROR",
+               "CCAE_CQ_SQ_COMPLETION_OVERFLOW",
+               "CCAE_CQ_RQ_COMPLETION_ERROR",
+               "CCAE_QP_SRQ_WQE_ERROR",
+               "CCAE_QP_LOCAL_CATASTROPHIC_ERROR",
+               "CCAE_CQ_OVERFLOW",
+               "CCAE_CQ_OPERATION_ERROR",
+               "CCAE_SRQ_LIMIT_REACHED",
+               "CCAE_QP_RQ_LIMIT_REACHED",
+               "CCAE_SRQ_CATASTROPHIC_ERROR",
+               "CCAE_RNIC_CATASTROPHIC_ERROR"
+       };
+
+       if (event < CCAE_REMOTE_SHUTDOWN ||
+           event > CCAE_RNIC_CATASTROPHIC_ERROR)
+               return "<invalid event>";
+
+       event -= CCAE_REMOTE_SHUTDOWN;
+       return event_str[event];
+}
+
+static const char *to_qp_state_str(int state)
+{
+       switch (state) {
+       case C2_QP_STATE_IDLE:
+               return "C2_QP_STATE_IDLE";
+       case C2_QP_STATE_CONNECTING:
+               return "C2_QP_STATE_CONNECTING";
+       case C2_QP_STATE_RTS:
+               return "C2_QP_STATE_RTS";
+       case C2_QP_STATE_CLOSING:
+               return "C2_QP_STATE_CLOSING";
+       case C2_QP_STATE_TERMINATE:
+               return "C2_QP_STATE_TERMINATE";
+       case C2_QP_STATE_ERROR:
+               return "C2_QP_STATE_ERROR";
+       default:
+               return "<invalid QP state>";
+       };
+}
+#endif
+
+void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
+{
+       struct c2_mq *mq = c2dev->qptr_array[mq_index];
+       union c2wr *wr;
+       void *resource_user_context;
+       struct iw_cm_event cm_event;
+       struct ib_event ib_event;
+       enum c2_resource_indicator resource_indicator;
+       enum c2_event_id event_id;
+       unsigned long flags;
+       int status;
+
+       /*
+        * retreive the message
+        */
+       wr = c2_mq_consume(mq);
+       if (!wr)
+               return;
+
+       memset(&ib_event, 0, sizeof(ib_event));
+       memset(&cm_event, 0, sizeof(cm_event));
+
+       event_id = c2_wr_get_id(wr);
+       resource_indicator = be32_to_cpu(wr->ae.ae_generic.resource_type);
+       resource_user_context =
+           (void *) (unsigned long) wr->ae.ae_generic.user_context;
+
+       status = cm_event.status = c2_convert_cm_status(c2_wr_get_result(wr));
+
+       pr_debug("event received c2_dev=%p, event_id=%d, "
+               "resource_indicator=%d, user_context=%p, status = %d\n",
+               c2dev, event_id, resource_indicator, resource_user_context,
+               status);
+
+       switch (resource_indicator) {
+       case C2_RES_IND_QP:{
+
+               struct c2_qp *qp = (struct c2_qp *)resource_user_context;
+               struct iw_cm_id *cm_id = qp->cm_id;
+               struct c2wr_ae_active_connect_results *res;
+
+               if (!cm_id) {
+                       pr_debug("event received, but cm_id is <nul>, qp=%p!\n",
+                               qp);
+                       goto ignore_it;
+               }
+               pr_debug("%s: event = %s, user_context=%llx, "
+                       "resource_type=%x, "
+                       "resource=%x, qp_state=%s\n",
+                       __FUNCTION__,
+                       to_event_str(event_id),
+                       be64_to_cpu(wr->ae.ae_generic.user_context),
+                       be32_to_cpu(wr->ae.ae_generic.resource_type),
+                       be32_to_cpu(wr->ae.ae_generic.resource),
+                       to_qp_state_str(be32_to_cpu(wr->ae.ae_generic.qp_state)));
+
+               c2_set_qp_state(qp, be32_to_cpu(wr->ae.ae_generic.qp_state));
+
+               switch (event_id) {
+               case CCAE_ACTIVE_CONNECT_RESULTS:
+                       res = &wr->ae.ae_active_connect_results;
+                       cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
+                       cm_event.local_addr.sin_addr.s_addr = res->laddr;
+                       cm_event.remote_addr.sin_addr.s_addr = res->raddr;
+                       cm_event.local_addr.sin_port = res->lport;
+                       cm_event.remote_addr.sin_port = res->rport;
+                       if (status == 0) {
+                               cm_event.private_data_len =
+                                       be32_to_cpu(res->private_data_length);
+                               cm_event.private_data = res->private_data;
+                       } else {
+                               spin_lock_irqsave(&qp->lock, flags);
+                               if (qp->cm_id) {
+                                       qp->cm_id->rem_ref(qp->cm_id);
+                                       qp->cm_id = NULL;
+                               }
+                               spin_unlock_irqrestore(&qp->lock, flags);
+                               cm_event.private_data_len = 0;
+                               cm_event.private_data = NULL;
+                       }
+                       if (cm_id->event_handler)
+                               cm_id->event_handler(cm_id, &cm_event);
+                       break;
+               case CCAE_TERMINATE_MESSAGE_RECEIVED:
+               case CCAE_CQ_SQ_COMPLETION_OVERFLOW:
+                       ib_event.device = &c2dev->ibdev;
+                       ib_event.element.qp = &qp->ibqp;
+                       ib_event.event = IB_EVENT_QP_REQ_ERR;
+
+                       if (qp->ibqp.event_handler)
+                               qp->ibqp.event_handler(&ib_event,
+                                                      qp->ibqp.
+                                                      qp_context);
+                       break;
+               case CCAE_BAD_CLOSE:
+               case CCAE_LLP_CLOSE_COMPLETE:
+               case CCAE_LLP_CONNECTION_RESET:
+               case CCAE_LLP_CONNECTION_LOST:
+                       BUG_ON(cm_id->event_handler==(void*)0x6b6b6b6b);
+
+                       spin_lock_irqsave(&qp->lock, flags);
+                       if (qp->cm_id) {
+                               qp->cm_id->rem_ref(qp->cm_id);
+                               qp->cm_id = NULL;
+                       }
+                       spin_unlock_irqrestore(&qp->lock, flags);
+                       cm_event.event = IW_CM_EVENT_CLOSE;
+                       cm_event.status = 0;
+                       if (cm_id->event_handler)
+                               cm_id->event_handler(cm_id, &cm_event);
+                       break;
+               default:
+                       BUG_ON(1);
+                       pr_debug("%s:%d Unexpected event_id=%d on QP=%p, "
+                               "CM_ID=%p\n",
+                               __FUNCTION__, __LINE__,
+                               event_id, qp, cm_id);
+                       break;
+               }
+               break;
+       }
+
+       case C2_RES_IND_EP:{
+
+               struct c2wr_ae_connection_request *req =
+                       &wr->ae.ae_connection_request;
+               struct iw_cm_id *cm_id =
+                       (struct iw_cm_id *)resource_user_context;
+
+               pr_debug("C2_RES_IND_EP event_id=%d\n", event_id);
+               if (event_id != CCAE_CONNECTION_REQUEST) {
+                       pr_debug("%s: Invalid event_id: %d\n",
+                               __FUNCTION__, event_id);
+                       break;
+               }
+               cm_event.event = IW_CM_EVENT_CONNECT_REQUEST;
+               cm_event.provider_data = (void*)(unsigned long)req->cr_handle;
+               cm_event.local_addr.sin_addr.s_addr = req->laddr;
+               cm_event.remote_addr.sin_addr.s_addr = req->raddr;
+               cm_event.local_addr.sin_port = req->lport;
+               cm_event.remote_addr.sin_port = req->rport;
+               cm_event.private_data_len =
+                       be32_to_cpu(req->private_data_length);
+               cm_event.private_data = req->private_data;
+
+               if (cm_id->event_handler)
+                       cm_id->event_handler(cm_id, &cm_event);
+               break;
+       }
+
+       case C2_RES_IND_CQ:{
+               struct c2_cq *cq =
+                   (struct c2_cq *) resource_user_context;
+
+               pr_debug("IB_EVENT_CQ_ERR\n");
+               ib_event.device = &c2dev->ibdev;
+               ib_event.element.cq = &cq->ibcq;
+               ib_event.event = IB_EVENT_CQ_ERR;
+
+               if (cq->ibcq.event_handler)
+                       cq->ibcq.event_handler(&ib_event,
+                                              cq->ibcq.cq_context);
+       }
+
+       default:
+               printk("Bad resource indicator = %d\n",
+                      resource_indicator);
+               break;
+       }
+
+ ignore_it:
+       c2_mq_free(mq);
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.h b/drivers/infiniband/hw/amso1100/c2_ae.h
new file mode 100644 (file)
index 0000000..3a065c3
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 _C2_AE_H_
+#define _C2_AE_H_
+
+/*
+ * WARNING: If you change this file, also bump C2_IVN_BASE
+ * in common/include/clustercore/c2_ivn.h.
+ */
+
+/*
+ * Asynchronous Event Identifiers
+ *
+ * These start at 0x80 only so it's obvious from inspection that
+ * they are not work-request statuses.  This isn't critical.
+ *
+ * NOTE: these event id's must fit in eight bits.
+ */
+enum c2_event_id {
+       CCAE_REMOTE_SHUTDOWN = 0x80,
+       CCAE_ACTIVE_CONNECT_RESULTS,
+       CCAE_CONNECTION_REQUEST,
+       CCAE_LLP_CLOSE_COMPLETE,
+       CCAE_TERMINATE_MESSAGE_RECEIVED,
+       CCAE_LLP_CONNECTION_RESET,
+       CCAE_LLP_CONNECTION_LOST,
+       CCAE_LLP_SEGMENT_SIZE_INVALID,
+       CCAE_LLP_INVALID_CRC,
+       CCAE_LLP_BAD_FPDU,
+       CCAE_INVALID_DDP_VERSION,
+       CCAE_INVALID_RDMA_VERSION,
+       CCAE_UNEXPECTED_OPCODE,
+       CCAE_INVALID_DDP_QUEUE_NUMBER,
+       CCAE_RDMA_READ_NOT_ENABLED,
+       CCAE_RDMA_WRITE_NOT_ENABLED,
+       CCAE_RDMA_READ_TOO_SMALL,
+       CCAE_NO_L_BIT,
+       CCAE_TAGGED_INVALID_STAG,
+       CCAE_TAGGED_BASE_BOUNDS_VIOLATION,
+       CCAE_TAGGED_ACCESS_RIGHTS_VIOLATION,
+       CCAE_TAGGED_INVALID_PD,
+       CCAE_WRAP_ERROR,
+       CCAE_BAD_CLOSE,
+       CCAE_BAD_LLP_CLOSE,
+       CCAE_INVALID_MSN_RANGE,
+       CCAE_INVALID_MSN_GAP,
+       CCAE_IRRQ_OVERFLOW,
+       CCAE_IRRQ_MSN_GAP,
+       CCAE_IRRQ_MSN_RANGE,
+       CCAE_IRRQ_INVALID_STAG,
+       CCAE_IRRQ_BASE_BOUNDS_VIOLATION,
+       CCAE_IRRQ_ACCESS_RIGHTS_VIOLATION,
+       CCAE_IRRQ_INVALID_PD,
+       CCAE_IRRQ_WRAP_ERROR,
+       CCAE_CQ_SQ_COMPLETION_OVERFLOW,
+       CCAE_CQ_RQ_COMPLETION_ERROR,
+       CCAE_QP_SRQ_WQE_ERROR,
+       CCAE_QP_LOCAL_CATASTROPHIC_ERROR,
+       CCAE_CQ_OVERFLOW,
+       CCAE_CQ_OPERATION_ERROR,
+       CCAE_SRQ_LIMIT_REACHED,
+       CCAE_QP_RQ_LIMIT_REACHED,
+       CCAE_SRQ_CATASTROPHIC_ERROR,
+       CCAE_RNIC_CATASTROPHIC_ERROR
+/* WARNING If you add more id's, make sure their values fit in eight bits. */
+};
+
+/*
+ * Resource Indicators and Identifiers
+ */
+enum c2_resource_indicator {
+       C2_RES_IND_QP = 1,
+       C2_RES_IND_EP,
+       C2_RES_IND_CQ,
+       C2_RES_IND_SRQ,
+};
+
+#endif /* _C2_AE_H_ */
diff --git a/drivers/infiniband/hw/amso1100/c2_alloc.c b/drivers/infiniband/hw/amso1100/c2_alloc.c
new file mode 100644 (file)
index 0000000..1d25299
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  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/errno.h>
+#include <linux/slab.h>
+#include <linux/bitmap.h>
+
+#include "c2.h"
+
+static int c2_alloc_mqsp_chunk(struct c2_dev *c2dev, gfp_t gfp_mask,
+                              struct sp_chunk **head)
+{
+       int i;
+       struct sp_chunk *new_head;
+
+       new_head = (struct sp_chunk *) __get_free_page(gfp_mask);
+       if (new_head == NULL)
+               return -ENOMEM;
+
+       new_head->dma_addr = dma_map_single(c2dev->ibdev.dma_device, new_head,
+                                           PAGE_SIZE, DMA_FROM_DEVICE);
+       pci_unmap_addr_set(new_head, mapping, new_head->dma_addr);
+
+       new_head->next = NULL;
+       new_head->head = 0;
+
+       /* build list where each index is the next free slot */
+       for (i = 0;
+            i < (PAGE_SIZE - sizeof(struct sp_chunk) -
+                 sizeof(u16)) / sizeof(u16) - 1;
+            i++) {
+               new_head->shared_ptr[i] = i + 1;
+       }
+       /* terminate list */
+       new_head->shared_ptr[i] = 0xFFFF;
+
+       *head = new_head;
+       return 0;
+}
+
+int c2_init_mqsp_pool(struct c2_dev *c2dev, gfp_t gfp_mask,
+                     struct sp_chunk **root)
+{
+       return c2_alloc_mqsp_chunk(c2dev, gfp_mask, root);
+}
+
+void c2_free_mqsp_pool(struct c2_dev *c2dev, struct sp_chunk *root)
+{
+       struct sp_chunk *next;
+
+       while (root) {
+               next = root->next;
+               dma_unmap_single(c2dev->ibdev.dma_device,
+                                pci_unmap_addr(root, mapping), PAGE_SIZE,
+                                DMA_FROM_DEVICE);
+               __free_page((struct page *) root);
+               root = next;
+       }
+}
+
+u16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head,
+                  dma_addr_t *dma_addr, gfp_t gfp_mask)
+{
+       u16 mqsp;
+
+       while (head) {
+               mqsp = head->head;
+               if (mqsp != 0xFFFF) {
+                       head->head = head->shared_ptr[mqsp];
+                       break;
+               } else if (head->next == NULL) {
+                       if (c2_alloc_mqsp_chunk(c2dev, gfp_mask, &head->next) ==
+                           0) {
+                               head = head->next;
+                               mqsp = head->head;
+                               head->head = head->shared_ptr[mqsp];
+                               break;
+                       } else
+                               return NULL;
+               } else
+                       head = head->next;
+       }
+       if (head) {
+               *dma_addr = head->dma_addr +
+                           ((unsigned long) &(head->shared_ptr[mqsp]) -
+                            (unsigned long) head);
+               pr_debug("%s addr %p dma_addr %llx\n", __FUNCTION__,
+                        &(head->shared_ptr[mqsp]), (u64)*dma_addr);
+               return &(head->shared_ptr[mqsp]);
+       }
+       return NULL;
+}
+
+void c2_free_mqsp(u16 * mqsp)
+{
+       struct sp_chunk *head;
+       u16 idx;
+
+       /* The chunk containing this ptr begins at the page boundary */
+       head = (struct sp_chunk *) ((unsigned long) mqsp & PAGE_MASK);
+
+       /* Link head to new mqsp */
+       *mqsp = head->head;
+
+       /* Compute the shared_ptr index */
+       idx = ((unsigned long) mqsp & ~PAGE_MASK) >> 1;
+       idx -= (unsigned long) &(((struct sp_chunk *) 0)->shared_ptr[0]) >> 1;
+
+       /* Point this index at the head */
+       head->shared_ptr[idx] = head->head;
+
+       /* Point head at this index */
+       head->head = idx;
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_cm.c b/drivers/infiniband/hw/amso1100/c2_cm.c
new file mode 100644 (file)
index 0000000..485254e
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 "c2.h"
+#include "c2_wr.h"
+#include "c2_vq.h"
+#include <rdma/iw_cm.h>
+
+int c2_llp_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
+{
+       struct c2_dev *c2dev = to_c2dev(cm_id->device);
+       struct ib_qp *ibqp;
+       struct c2_qp *qp;
+       struct c2wr_qp_connect_req *wr; /* variable size needs a malloc. */
+       struct c2_vq_req *vq_req;
+       int err;
+
+       ibqp = c2_get_qp(cm_id->device, iw_param->qpn);
+       if (!ibqp)
+               return -EINVAL;
+       qp = to_c2qp(ibqp);
+
+       /* Associate QP <--> CM_ID */
+       cm_id->provider_data = qp;
+       cm_id->add_ref(cm_id);
+       qp->cm_id = cm_id;
+
+       /*
+        * only support the max private_data length
+        */
+       if (iw_param->private_data_len > C2_MAX_PRIVATE_DATA_SIZE) {
+               err = -EINVAL;
+               goto bail0;
+       }
+       /*
+        * Set the rdma read limits
+        */
+       err = c2_qp_set_read_limits(c2dev, qp, iw_param->ord, iw_param->ird);
+       if (err)
+               goto bail0;
+
+       /*
+        * Create and send a WR_QP_CONNECT...
+        */
+       wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
+       if (!wr) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       c2_wr_set_id(wr, CCWR_QP_CONNECT);
+       wr->hdr.context = 0;
+       wr->rnic_handle = c2dev->adapter_handle;
+       wr->qp_handle = qp->adapter_handle;
+
+       wr->remote_addr = cm_id->remote_addr.sin_addr.s_addr;
+       wr->remote_port = cm_id->remote_addr.sin_port;
+
+       /*
+        * Move any private data from the callers's buf into
+        * the WR.
+        */
+       if (iw_param->private_data) {
+               wr->private_data_length =
+                       cpu_to_be32(iw_param->private_data_len);
+               memcpy(&wr->private_data[0], iw_param->private_data,
+                      iw_param->private_data_len);
+       } else
+               wr->private_data_length = 0;
+
+       /*
+        * Send WR to adapter.  NOTE: There is no synch reply from
+        * the adapter.
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) wr);
+       vq_req_free(c2dev, vq_req);
+
+ bail1:
+       kfree(wr);
+ bail0:
+       if (err) {
+               /*
+                * If we fail, release reference on QP and
+                * disassociate QP from CM_ID
+                */
+               cm_id->provider_data = NULL;
+               qp->cm_id = NULL;
+               cm_id->rem_ref(cm_id);
+       }
+       return err;
+}
+
+int c2_llp_service_create(struct iw_cm_id *cm_id, int backlog)
+{
+       struct c2_dev *c2dev;
+       struct c2wr_ep_listen_create_req wr;
+       struct c2wr_ep_listen_create_rep *reply;
+       struct c2_vq_req *vq_req;
+       int err;
+
+       c2dev = to_c2dev(cm_id->device);
+       if (c2dev == NULL)
+               return -EINVAL;
+
+       /*
+        * Allocate verbs request.
+        */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       /*
+        * Build the WR
+        */
+       c2_wr_set_id(&wr, CCWR_EP_LISTEN_CREATE);
+       wr.hdr.context = (u64) (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.local_addr = cm_id->local_addr.sin_addr.s_addr;
+       wr.local_port = cm_id->local_addr.sin_port;
+       wr.backlog = cpu_to_be32(backlog);
+       wr.user_context = (u64) (unsigned long) cm_id;
+
+       /*
+        * Reference the request struct.  Dereferenced in the int handler.
+        */
+       vq_req_get(c2dev, vq_req);
+
+       /*
+        * Send WR to adapter
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       /*
+        * Wait for reply from adapter
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail0;
+
+       /*
+        * Process reply
+        */
+       reply =
+           (struct c2wr_ep_listen_create_rep *) (unsigned long) vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       if ((err = c2_errno(reply)) != 0)
+               goto bail1;
+
+       /*
+        * Keep the adapter handle. Used in subsequent destroy
+        */
+       cm_id->provider_data = (void*)(unsigned long) reply->ep_handle;
+
+       /*
+        * free vq stuff
+        */
+       vq_repbuf_free(c2dev, reply);
+       vq_req_free(c2dev, vq_req);
+
+       return 0;
+
+ bail1:
+       vq_repbuf_free(c2dev, reply);
+ bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+
+int c2_llp_service_destroy(struct iw_cm_id *cm_id)
+{
+
+       struct c2_dev *c2dev;
+       struct c2wr_ep_listen_destroy_req wr;
+       struct c2wr_ep_listen_destroy_rep *reply;
+       struct c2_vq_req *vq_req;
+       int err;
+
+       c2dev = to_c2dev(cm_id->device);
+       if (c2dev == NULL)
+               return -EINVAL;
+
+       /*
+        * Allocate verbs request.
+        */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       /*
+        * Build the WR
+        */
+       c2_wr_set_id(&wr, CCWR_EP_LISTEN_DESTROY);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.ep_handle = (u32)(unsigned long)cm_id->provider_data;
+
+       /*
+        * reference the request struct.  dereferenced in the int handler.
+        */
+       vq_req_get(c2dev, vq_req);
+
+       /*
+        * Send WR to adapter
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       /*
+        * Wait for reply from adapter
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail0;
+
+       /*
+        * Process reply
+        */
+       reply=(struct c2wr_ep_listen_destroy_rep *)(unsigned long)vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+       if ((err = c2_errno(reply)) != 0)
+               goto bail1;
+
+ bail1:
+       vq_repbuf_free(c2dev, reply);
+ bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
+{
+       struct c2_dev *c2dev = to_c2dev(cm_id->device);
+       struct c2_qp *qp;
+       struct ib_qp *ibqp;
+       struct c2wr_cr_accept_req *wr;  /* variable length WR */
+       struct c2_vq_req *vq_req;
+       struct c2wr_cr_accept_rep *reply;       /* VQ Reply msg ptr. */
+       int err;
+
+       ibqp = c2_get_qp(cm_id->device, iw_param->qpn);
+       if (!ibqp)
+               return -EINVAL;
+       qp = to_c2qp(ibqp);
+
+       /* Set the RDMA read limits */
+       err = c2_qp_set_read_limits(c2dev, qp, iw_param->ord, iw_param->ird);
+       if (err)
+               goto bail0;
+
+       /* Allocate verbs request. */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+       vq_req->qp = qp;
+       vq_req->cm_id = cm_id;
+       vq_req->event = IW_CM_EVENT_ESTABLISHED;
+
+       wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
+       if (!wr) {
+               err = -ENOMEM;
+               goto bail2;
+       }
+
+       /* Build the WR */
+       c2_wr_set_id(wr, CCWR_CR_ACCEPT);
+       wr->hdr.context = (unsigned long) vq_req;
+       wr->rnic_handle = c2dev->adapter_handle;
+       wr->ep_handle = (u32) (unsigned long) cm_id->provider_data;
+       wr->qp_handle = qp->adapter_handle;
+
+       /* Replace the cr_handle with the QP after accept */
+       cm_id->provider_data = qp;
+       cm_id->add_ref(cm_id);
+       qp->cm_id = cm_id;
+
+       cm_id->provider_data = qp;
+
+       /* Validate private_data length */
+       if (iw_param->private_data_len > C2_MAX_PRIVATE_DATA_SIZE) {
+               err = -EINVAL;
+               goto bail2;
+       }
+
+       if (iw_param->private_data) {
+               wr->private_data_length = cpu_to_be32(iw_param->private_data_len);
+               memcpy(&wr->private_data[0],
+                      iw_param->private_data, iw_param->private_data_len);
+       } else
+               wr->private_data_length = 0;
+
+       /* Reference the request struct.  Dereferenced in the int handler. */
+       vq_req_get(c2dev, vq_req);
+
+       /* Send WR to adapter */
+       err = vq_send_wr(c2dev, (union c2wr *) wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail2;
+       }
+
+       /* Wait for reply from adapter */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail2;
+
+       /* Check that reply is present */
+       reply = (struct c2wr_cr_accept_rep *) (unsigned long) vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail2;
+       }
+
+       err = c2_errno(reply);
+       vq_repbuf_free(c2dev, reply);
+
+       if (!err)
+               c2_set_qp_state(qp, C2_QP_STATE_RTS);
+ bail2:
+       kfree(wr);
+ bail1:
+       vq_req_free(c2dev, vq_req);
+ bail0:
+       if (err) {
+               /*
+                * If we fail, release reference on QP and
+                * disassociate QP from CM_ID
+                */
+               cm_id->provider_data = NULL;
+               qp->cm_id = NULL;
+               cm_id->rem_ref(cm_id);
+       }
+       return err;
+}
+
+int c2_llp_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
+{
+       struct c2_dev *c2dev;
+       struct c2wr_cr_reject_req wr;
+       struct c2_vq_req *vq_req;
+       struct c2wr_cr_reject_rep *reply;
+       int err;
+
+       c2dev = to_c2dev(cm_id->device);
+
+       /*
+        * Allocate verbs request.
+        */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       /*
+        * Build the WR
+        */
+       c2_wr_set_id(&wr, CCWR_CR_REJECT);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.ep_handle = (u32) (unsigned long) cm_id->provider_data;
+
+       /*
+        * reference the request struct.  dereferenced in the int handler.
+        */
+       vq_req_get(c2dev, vq_req);
+
+       /*
+        * Send WR to adapter
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       /*
+        * Wait for reply from adapter
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail0;
+
+       /*
+        * Process reply
+        */
+       reply = (struct c2wr_cr_reject_rep *) (unsigned long)
+               vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+       err = c2_errno(reply);
+       /*
+        * free vq stuff
+        */
+       vq_repbuf_free(c2dev, reply);
+
+ bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_cq.c b/drivers/infiniband/hw/amso1100/c2_cq.c
new file mode 100644 (file)
index 0000000..9d7bcc5
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2005 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2004 Voltaire, 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 "c2.h"
+#include "c2_vq.h"
+#include "c2_status.h"
+
+#define C2_CQ_MSG_SIZE ((sizeof(struct c2wr_ce) + 32-1) & ~(32-1))
+
+static struct c2_cq *c2_cq_get(struct c2_dev *c2dev, int cqn)
+{
+       struct c2_cq *cq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&c2dev->lock, flags);
+       cq = c2dev->qptr_array[cqn];
+       if (!cq) {
+               spin_unlock_irqrestore(&c2dev->lock, flags);
+               return NULL;
+       }
+       atomic_inc(&cq->refcount);
+       spin_unlock_irqrestore(&c2dev->lock, flags);
+       return cq;
+}
+
+static void c2_cq_put(struct c2_cq *cq)
+{
+       if (atomic_dec_and_test(&cq->refcount))
+               wake_up(&cq->wait);
+}
+
+void c2_cq_event(struct c2_dev *c2dev, u32 mq_index)
+{
+       struct c2_cq *cq;
+
+       cq = c2_cq_get(c2dev, mq_index);
+       if (!cq) {
+               printk("discarding events on destroyed CQN=%d\n", mq_index);
+               return;
+       }
+
+       (*cq->ibcq.comp_handler) (&cq->ibcq, cq->ibcq.cq_context);
+       c2_cq_put(cq);
+}
+
+void c2_cq_clean(struct c2_dev *c2dev, struct c2_qp *qp, u32 mq_index)
+{
+       struct c2_cq *cq;
+       struct c2_mq *q;
+
+       cq = c2_cq_get(c2dev, mq_index);
+       if (!cq)
+               return;
+
+       spin_lock_irq(&cq->lock);
+       q = &cq->mq;
+       if (q && !c2_mq_empty(q)) {
+               u16 priv = q->priv;
+               struct c2wr_ce *msg;
+
+               while (priv != be16_to_cpu(*q->shared)) {
+                       msg = (struct c2wr_ce *)
+                               (q->msg_pool.host + priv * q->msg_size);
+                       if (msg->qp_user_context == (u64) (unsigned long) qp) {
+                               msg->qp_user_context = (u64) 0;
+                       }
+                       priv = (priv + 1) % q->q_size;
+               }
+       }
+       spin_unlock_irq(&cq->lock);
+       c2_cq_put(cq);
+}
+
+static inline enum ib_wc_status c2_cqe_status_to_openib(u8 status)
+{
+       switch (status) {
+       case C2_OK:
+               return IB_WC_SUCCESS;
+       case CCERR_FLUSHED:
+               return IB_WC_WR_FLUSH_ERR;
+       case CCERR_BASE_AND_BOUNDS_VIOLATION:
+               return IB_WC_LOC_PROT_ERR;
+       case CCERR_ACCESS_VIOLATION:
+               return IB_WC_LOC_ACCESS_ERR;
+       case CCERR_TOTAL_LENGTH_TOO_BIG:
+               return IB_WC_LOC_LEN_ERR;
+       case CCERR_INVALID_WINDOW:
+               return IB_WC_MW_BIND_ERR;
+       default:
+               return IB_WC_GENERAL_ERR;
+       }
+}
+
+
+static inline int c2_poll_one(struct c2_dev *c2dev,
+                             struct c2_cq *cq, struct ib_wc *entry)
+{
+       struct c2wr_ce *ce;
+       struct c2_qp *qp;
+       int is_recv = 0;
+
+       ce = (struct c2wr_ce *) c2_mq_consume(&cq->mq);
+       if (!ce) {
+               return -EAGAIN;
+       }
+
+       /*
+        * if the qp returned is null then this qp has already
+        * been freed and we are unable process the completion.
+        * try pulling the next message
+        */
+       while ((qp =
+               (struct c2_qp *) (unsigned long) ce->qp_user_context) == NULL) {
+               c2_mq_free(&cq->mq);
+               ce = (struct c2wr_ce *) c2_mq_consume(&cq->mq);
+               if (!ce)
+                       return -EAGAIN;
+       }
+
+       entry->status = c2_cqe_status_to_openib(c2_wr_get_result(ce));
+       entry->wr_id = ce->hdr.context;
+       entry->qp_num = ce->handle;
+       entry->wc_flags = 0;
+       entry->slid = 0;
+       entry->sl = 0;
+       entry->src_qp = 0;
+       entry->dlid_path_bits = 0;
+       entry->pkey_index = 0;
+
+       switch (c2_wr_get_id(ce)) {
+       case C2_WR_TYPE_SEND:
+               entry->opcode = IB_WC_SEND;
+               break;
+       case C2_WR_TYPE_RDMA_WRITE:
+               entry->opcode = IB_WC_RDMA_WRITE;
+               break;
+       case C2_WR_TYPE_RDMA_READ:
+               entry->opcode = IB_WC_RDMA_READ;
+               break;
+       case C2_WR_TYPE_BIND_MW:
+               entry->opcode = IB_WC_BIND_MW;
+               break;
+       case C2_WR_TYPE_RECV:
+               entry->byte_len = be32_to_cpu(ce->bytes_rcvd);
+               entry->opcode = IB_WC_RECV;
+               is_recv = 1;
+               break;
+       default:
+               break;
+       }
+
+       /* consume the WQEs */
+       if (is_recv)
+               c2_mq_lconsume(&qp->rq_mq, 1);
+       else
+               c2_mq_lconsume(&qp->sq_mq,
+                              be32_to_cpu(c2_wr_get_wqe_count(ce)) + 1);
+
+       /* free the message */
+       c2_mq_free(&cq->mq);
+
+       return 0;
+}
+
+int c2_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
+{
+       struct c2_dev *c2dev = to_c2dev(ibcq->device);
+       struct c2_cq *cq = to_c2cq(ibcq);
+       unsigned long flags;
+       int npolled, err;
+
+       spin_lock_irqsave(&cq->lock, flags);
+
+       for (npolled = 0; npolled < num_entries; ++npolled) {
+
+               err = c2_poll_one(c2dev, cq, entry + npolled);
+               if (err)
+                       break;
+       }
+
+       spin_unlock_irqrestore(&cq->lock, flags);
+
+       return npolled;
+}
+
+int c2_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify notify)
+{
+       struct c2_mq_shared __iomem *shared;
+       struct c2_cq *cq;
+
+       cq = to_c2cq(ibcq);
+       shared = cq->mq.peer;
+
+       if (notify == IB_CQ_NEXT_COMP)
+               writeb(C2_CQ_NOTIFICATION_TYPE_NEXT, &shared->notification_type);
+       else if (notify == IB_CQ_SOLICITED)
+               writeb(C2_CQ_NOTIFICATION_TYPE_NEXT_SE, &shared->notification_type);
+       else
+               return -EINVAL;
+
+       writeb(CQ_WAIT_FOR_DMA | CQ_ARMED, &shared->armed);
+
+       /*
+        * Now read back shared->armed to make the PCI
+        * write synchronous.  This is necessary for
+        * correct cq notification semantics.
+        */
+       readb(&shared->armed);
+
+       return 0;
+}
+
+static void c2_free_cq_buf(struct c2_dev *c2dev, struct c2_mq *mq)
+{
+
+       dma_unmap_single(c2dev->ibdev.dma_device, pci_unmap_addr(mq, mapping),
+                        mq->q_size * mq->msg_size, DMA_FROM_DEVICE);
+       free_pages((unsigned long) mq->msg_pool.host,
+                  get_order(mq->q_size * mq->msg_size));
+}
+
+static int c2_alloc_cq_buf(struct c2_dev *c2dev, struct c2_mq *mq, int q_size,
+                          int msg_size)
+{
+       unsigned long pool_start;
+
+       pool_start = __get_free_pages(GFP_KERNEL,
+                                     get_order(q_size * msg_size));
+       if (!pool_start)
+               return -ENOMEM;
+
+       c2_mq_rep_init(mq,
+                      0,               /* index (currently unknown) */
+                      q_size,
+                      msg_size,
+                      (u8 *) pool_start,
+                      NULL,    /* peer (currently unknown) */
+                      C2_MQ_HOST_TARGET);
+
+       mq->host_dma = dma_map_single(c2dev->ibdev.dma_device,
+                                     (void *)pool_start,
+                                     q_size * msg_size, DMA_FROM_DEVICE);
+       pci_unmap_addr_set(mq, mapping, mq->host_dma);
+
+       return 0;
+}
+
+int c2_init_cq(struct c2_dev *c2dev, int entries,
+              struct c2_ucontext *ctx, struct c2_cq *cq)
+{
+       struct c2wr_cq_create_req wr;
+       struct c2wr_cq_create_rep *reply;
+       unsigned long peer_pa;
+       struct c2_vq_req *vq_req;
+       int err;
+
+       might_sleep();
+
+       cq->ibcq.cqe = entries - 1;
+       cq->is_kernel = !ctx;
+
+       /* Allocate a shared pointer */
+       cq->mq.shared = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                     &cq->mq.shared_dma, GFP_KERNEL);
+       if (!cq->mq.shared)
+               return -ENOMEM;
+
+       /* Allocate pages for the message pool */
+       err = c2_alloc_cq_buf(c2dev, &cq->mq, entries + 1, C2_CQ_MSG_SIZE);
+       if (err)
+               goto bail0;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_CQ_CREATE);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.msg_size = cpu_to_be32(cq->mq.msg_size);
+       wr.depth = cpu_to_be32(cq->mq.q_size);
+       wr.shared_ht = cpu_to_be64(cq->mq.shared_dma);
+       wr.msg_pool = cpu_to_be64(cq->mq.host_dma);
+       wr.user_context = (u64) (unsigned long) (cq);
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail2;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail2;
+
+       reply = (struct c2wr_cq_create_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail2;
+       }
+
+       if ((err = c2_errno(reply)) != 0)
+               goto bail3;
+
+       cq->adapter_handle = reply->cq_handle;
+       cq->mq.index = be32_to_cpu(reply->mq_index);
+
+       peer_pa = c2dev->pa + be32_to_cpu(reply->adapter_shared);
+       cq->mq.peer = ioremap_nocache(peer_pa, PAGE_SIZE);
+       if (!cq->mq.peer) {
+               err = -ENOMEM;
+               goto bail3;
+       }
+
+       vq_repbuf_free(c2dev, reply);
+       vq_req_free(c2dev, vq_req);
+
+       spin_lock_init(&cq->lock);
+       atomic_set(&cq->refcount, 1);
+       init_waitqueue_head(&cq->wait);
+
+       /*
+        * Use the MQ index allocated by the adapter to
+        * store the CQ in the qptr_array
+        */
+       cq->cqn = cq->mq.index;
+       c2dev->qptr_array[cq->cqn] = cq;
+
+       return 0;
+
+      bail3:
+       vq_repbuf_free(c2dev, reply);
+      bail2:
+       vq_req_free(c2dev, vq_req);
+      bail1:
+       c2_free_cq_buf(c2dev, &cq->mq);
+      bail0:
+       c2_free_mqsp(cq->mq.shared);
+
+       return err;
+}
+
+void c2_free_cq(struct c2_dev *c2dev, struct c2_cq *cq)
+{
+       int err;
+       struct c2_vq_req *vq_req;
+       struct c2wr_cq_destroy_req wr;
+       struct c2wr_cq_destroy_rep *reply;
+
+       might_sleep();
+
+       /* Clear CQ from the qptr array */
+       spin_lock_irq(&c2dev->lock);
+       c2dev->qptr_array[cq->mq.index] = NULL;
+       atomic_dec(&cq->refcount);
+       spin_unlock_irq(&c2dev->lock);
+
+       wait_event(cq->wait, !atomic_read(&cq->refcount));
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req) {
+               goto bail0;
+       }
+
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_CQ_DESTROY);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.cq_handle = cq->adapter_handle;
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail1;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail1;
+
+       reply = (struct c2wr_cq_destroy_rep *) (unsigned long) (vq_req->reply_msg);
+
+       vq_repbuf_free(c2dev, reply);
+      bail1:
+       vq_req_free(c2dev, vq_req);
+      bail0:
+       if (cq->is_kernel) {
+               c2_free_cq_buf(c2dev, &cq->mq);
+       }
+
+       return;
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_intr.c b/drivers/infiniband/hw/amso1100/c2_intr.c
new file mode 100644 (file)
index 0000000..0d0bc33
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 "c2.h"
+#include <rdma/iw_cm.h>
+#include "c2_vq.h"
+
+static void handle_mq(struct c2_dev *c2dev, u32 index);
+static void handle_vq(struct c2_dev *c2dev, u32 mq_index);
+
+/*
+ * Handle RNIC interrupts
+ */
+void c2_rnic_interrupt(struct c2_dev *c2dev)
+{
+       unsigned int mq_index;
+
+       while (c2dev->hints_read != be16_to_cpu(*c2dev->hint_count)) {
+               mq_index = readl(c2dev->regs + PCI_BAR0_HOST_HINT);
+               if (mq_index & 0x80000000) {
+                       break;
+               }
+
+               c2dev->hints_read++;
+               handle_mq(c2dev, mq_index);
+       }
+
+}
+
+/*
+ * Top level MQ handler
+ */
+static void handle_mq(struct c2_dev *c2dev, u32 mq_index)
+{
+       if (c2dev->qptr_array[mq_index] == NULL) {
+               pr_debug(KERN_INFO "handle_mq: stray activity for mq_index=%d\n",
+                       mq_index);
+               return;
+       }
+
+       switch (mq_index) {
+       case (0):
+               /*
+                * An index of 0 in the activity queue
+                * indicates the req vq now has messages
+                * available...
+                *
+                * Wake up any waiters waiting on req VQ
+                * message availability.
+                */
+               wake_up(&c2dev->req_vq_wo);
+               break;
+       case (1):
+               handle_vq(c2dev, mq_index);
+               break;
+       case (2):
+               /* We have to purge the VQ in case there are pending
+                * accept reply requests that would result in the
+                * generation of an ESTABLISHED event. If we don't
+                * generate these first, a CLOSE event could end up
+                * being delivered before the ESTABLISHED event.
+                */
+               handle_vq(c2dev, 1);
+
+               c2_ae_event(c2dev, mq_index);
+               break;
+       default:
+               /* There is no event synchronization between CQ events
+                * and AE or CM events. In fact, CQE could be
+                * delivered for all of the I/O up to and including the
+                * FLUSH for a peer disconenct prior to the ESTABLISHED
+                * event being delivered to the app. The reason for this
+                * is that CM events are delivered on a thread, while AE
+                * and CM events are delivered on interrupt context.
+                */
+               c2_cq_event(c2dev, mq_index);
+               break;
+       }
+
+       return;
+}
+
+/*
+ * Handles verbs WR replies.
+ */
+static void handle_vq(struct c2_dev *c2dev, u32 mq_index)
+{
+       void *adapter_msg, *reply_msg;
+       struct c2wr_hdr *host_msg;
+       struct c2wr_hdr tmp;
+       struct c2_mq *reply_vq;
+       struct c2_vq_req *req;
+       struct iw_cm_event cm_event;
+       int err;
+
+       reply_vq = (struct c2_mq *) c2dev->qptr_array[mq_index];
+
+       /*
+        * get next msg from mq_index into adapter_msg.
+        * don't free it yet.
+        */
+       adapter_msg = c2_mq_consume(reply_vq);
+       if (adapter_msg == NULL) {
+               return;
+       }
+
+       host_msg = vq_repbuf_alloc(c2dev);
+
+       /*
+        * If we can't get a host buffer, then we'll still
+        * wakeup the waiter, we just won't give him the msg.
+        * It is assumed the waiter will deal with this...
+        */
+       if (!host_msg) {
+               pr_debug("handle_vq: no repbufs!\n");
+
+               /*
+                * just copy the WR header into a local variable.
+                * this allows us to still demux on the context
+                */
+               host_msg = &tmp;
+               memcpy(host_msg, adapter_msg, sizeof(tmp));
+               reply_msg = NULL;
+       } else {
+               memcpy(host_msg, adapter_msg, reply_vq->msg_size);
+               reply_msg = host_msg;
+       }
+
+       /*
+        * consume the msg from the MQ
+        */
+       c2_mq_free(reply_vq);
+
+       /*
+        * wakeup the waiter.
+        */
+       req = (struct c2_vq_req *) (unsigned long) host_msg->context;
+       if (req == NULL) {
+               /*
+                * We should never get here, as the adapter should
+                * never send us a reply that we're not expecting.
+                */
+               vq_repbuf_free(c2dev, host_msg);
+               pr_debug("handle_vq: UNEXPECTEDLY got NULL req\n");
+               return;
+       }
+
+       err = c2_errno(reply_msg);
+       if (!err) switch (req->event) {
+       case IW_CM_EVENT_ESTABLISHED:
+               c2_set_qp_state(req->qp,
+                               C2_QP_STATE_RTS);
+       case IW_CM_EVENT_CLOSE:
+
+               /*
+                * Move the QP to RTS if this is
+                * the established event
+                */
+               cm_event.event = req->event;
+               cm_event.status = 0;
+               cm_event.local_addr = req->cm_id->local_addr;
+               cm_event.remote_addr = req->cm_id->remote_addr;
+               cm_event.private_data = NULL;
+               cm_event.private_data_len = 0;
+               req->cm_id->event_handler(req->cm_id, &cm_event);
+               break;
+       default:
+               break;
+       }
+
+       req->reply_msg = (u64) (unsigned long) (reply_msg);
+       atomic_set(&req->reply_ready, 1);
+       wake_up(&req->wait_object);
+
+       /*
+        * If the request was cancelled, then this put will
+        * free the vq_req memory...and reply_msg!!!
+        */
+       vq_req_put(c2dev, req);
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_mm.c b/drivers/infiniband/hw/amso1100/c2_mm.c
new file mode 100644 (file)
index 0000000..1e4f464
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 "c2.h"
+#include "c2_vq.h"
+
+#define PBL_VIRT 1
+#define PBL_PHYS 2
+
+/*
+ * Send all the PBL messages to convey the remainder of the PBL
+ * Wait for the adapter's reply on the last one.
+ * This is indicated by setting the MEM_PBL_COMPLETE in the flags.
+ *
+ * NOTE:  vq_req is _not_ freed by this function.  The VQ Host
+ *       Reply buffer _is_ freed by this function.
+ */
+static int
+send_pbl_messages(struct c2_dev *c2dev, u32 stag_index,
+                 unsigned long va, u32 pbl_depth,
+                 struct c2_vq_req *vq_req, int pbl_type)
+{
+       u32 pbe_count;          /* amt that fits in a PBL msg */
+       u32 count;              /* amt in this PBL MSG. */
+       struct c2wr_nsmr_pbl_req *wr;   /* PBL WR ptr */
+       struct c2wr_nsmr_pbl_rep *reply;        /* reply ptr */
+       int err, pbl_virt, pbl_index, i;
+
+       switch (pbl_type) {
+       case PBL_VIRT:
+               pbl_virt = 1;
+               break;
+       case PBL_PHYS:
+               pbl_virt = 0;
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+
+       pbe_count = (c2dev->req_vq.msg_size -
+                    sizeof(struct c2wr_nsmr_pbl_req)) / sizeof(u64);
+       wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
+       if (!wr) {
+               return -ENOMEM;
+       }
+       c2_wr_set_id(wr, CCWR_NSMR_PBL);
+
+       /*
+        * Only the last PBL message will generate a reply from the verbs,
+        * so we set the context to 0 indicating there is no kernel verbs
+        * handler blocked awaiting this reply.
+        */
+       wr->hdr.context = 0;
+       wr->rnic_handle = c2dev->adapter_handle;
+       wr->stag_index = stag_index;    /* already swapped */
+       wr->flags = 0;
+       pbl_index = 0;
+       while (pbl_depth) {
+               count = min(pbe_count, pbl_depth);
+               wr->addrs_length = cpu_to_be32(count);
+
+               /*
+                *  If this is the last message, then reference the
+                *  vq request struct cuz we're gonna wait for a reply.
+                *  also make this PBL msg as the last one.
+                */
+               if (count == pbl_depth) {
+                       /*
+                        * reference the request struct.  dereferenced in the
+                        * int handler.
+                        */
+                       vq_req_get(c2dev, vq_req);
+                       wr->flags = cpu_to_be32(MEM_PBL_COMPLETE);
+
+                       /*
+                        * This is the last PBL message.
+                        * Set the context to our VQ Request Object so we can
+                        * wait for the reply.
+                        */
+                       wr->hdr.context = (unsigned long) vq_req;
+               }
+
+               /*
+                * If pbl_virt is set then va is a virtual address
+                * that describes a virtually contiguous memory
+                * allocation. The wr needs the start of each virtual page
+                * to be converted to the corresponding physical address
+                * of the page. If pbl_virt is not set then va is an array
+                * of physical addresses and there is no conversion to do.
+                * Just fill in the wr with what is in the array.
+                */
+               for (i = 0; i < count; i++) {
+                       if (pbl_virt) {
+                               va += PAGE_SIZE;
+                       } else {
+                               wr->paddrs[i] =
+                                   cpu_to_be64(((u64 *)va)[pbl_index + i]);
+                       }
+               }
+
+               /*
+                * Send WR to adapter
+                */
+               err = vq_send_wr(c2dev, (union c2wr *) wr);
+               if (err) {
+                       if (count <= pbe_count) {
+                               vq_req_put(c2dev, vq_req);
+                       }
+                       goto bail0;
+               }
+               pbl_depth -= count;
+               pbl_index += count;
+       }
+
+       /*
+        *  Now wait for the reply...
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail0;
+       }
+
+       /*
+        * Process reply
+        */
+       reply = (struct c2wr_nsmr_pbl_rep *) (unsigned long) vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       err = c2_errno(reply);
+
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       kfree(wr);
+       return err;
+}
+
+#define C2_PBL_MAX_DEPTH 131072
+int
+c2_nsmr_register_phys_kern(struct c2_dev *c2dev, u64 *addr_list,
+                          int page_size, int pbl_depth, u32 length,
+                          u32 offset, u64 *va, enum c2_acf acf,
+                          struct c2_mr *mr)
+{
+       struct c2_vq_req *vq_req;
+       struct c2wr_nsmr_register_req *wr;
+       struct c2wr_nsmr_register_rep *reply;
+       u16 flags;
+       int i, pbe_count, count;
+       int err;
+
+       if (!va || !length || !addr_list || !pbl_depth)
+               return -EINTR;
+
+       /*
+        * Verify PBL depth is within rnic max
+        */
+       if (pbl_depth > C2_PBL_MAX_DEPTH) {
+               return -EINTR;
+       }
+
+       /*
+        * allocate verbs request object
+        */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
+       if (!wr) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       /*
+        * build the WR
+        */
+       c2_wr_set_id(wr, CCWR_NSMR_REGISTER);
+       wr->hdr.context = (unsigned long) vq_req;
+       wr->rnic_handle = c2dev->adapter_handle;
+
+       flags = (acf | MEM_VA_BASED | MEM_REMOTE);
+
+       /*
+        * compute how many pbes can fit in the message
+        */
+       pbe_count = (c2dev->req_vq.msg_size -
+                    sizeof(struct c2wr_nsmr_register_req)) / sizeof(u64);
+
+       if (pbl_depth <= pbe_count) {
+               flags |= MEM_PBL_COMPLETE;
+       }
+       wr->flags = cpu_to_be16(flags);
+       wr->stag_key = 0;       //stag_key;
+       wr->va = cpu_to_be64(*va);
+       wr->pd_id = mr->pd->pd_id;
+       wr->pbe_size = cpu_to_be32(page_size);
+       wr->length = cpu_to_be32(length);
+       wr->pbl_depth = cpu_to_be32(pbl_depth);
+       wr->fbo = cpu_to_be32(offset);
+       count = min(pbl_depth, pbe_count);
+       wr->addrs_length = cpu_to_be32(count);
+
+       /*
+        * fill out the PBL for this message
+        */
+       for (i = 0; i < count; i++) {
+               wr->paddrs[i] = cpu_to_be64(addr_list[i]);
+       }
+
+       /*
+        * regerence the request struct
+        */
+       vq_req_get(c2dev, vq_req);
+
+       /*
+        * send the WR to the adapter
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail1;
+       }
+
+       /*
+        * wait for reply from adapter
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail1;
+       }
+
+       /*
+        * process reply
+        */
+       reply =
+           (struct c2wr_nsmr_register_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+       if ((err = c2_errno(reply))) {
+               goto bail2;
+       }
+       //*p_pb_entries = be32_to_cpu(reply->pbl_depth);
+       mr->ibmr.lkey = mr->ibmr.rkey = be32_to_cpu(reply->stag_index);
+       vq_repbuf_free(c2dev, reply);
+
+       /*
+        * if there are still more PBEs we need to send them to
+        * the adapter and wait for a reply on the final one.
+        * reuse vq_req for this purpose.
+        */
+       pbl_depth -= count;
+       if (pbl_depth) {
+
+               vq_req->reply_msg = (unsigned long) NULL;
+               atomic_set(&vq_req->reply_ready, 0);
+               err = send_pbl_messages(c2dev,
+                                       cpu_to_be32(mr->ibmr.lkey),
+                                       (unsigned long) &addr_list[i],
+                                       pbl_depth, vq_req, PBL_PHYS);
+               if (err) {
+                       goto bail1;
+               }
+       }
+
+       vq_req_free(c2dev, vq_req);
+       kfree(wr);
+
+       return err;
+
+      bail2:
+       vq_repbuf_free(c2dev, reply);
+      bail1:
+       kfree(wr);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+int c2_stag_dealloc(struct c2_dev *c2dev, u32 stag_index)
+{
+       struct c2_vq_req *vq_req;       /* verbs request object */
+       struct c2wr_stag_dealloc_req wr;        /* work request */
+       struct c2wr_stag_dealloc_rep *reply;    /* WR reply  */
+       int err;
+
+
+       /*
+        * allocate verbs request object
+        */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req) {
+               return -ENOMEM;
+       }
+
+       /*
+        * Build the WR
+        */
+       c2_wr_set_id(&wr, CCWR_STAG_DEALLOC);
+       wr.hdr.context = (u64) (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.stag_index = cpu_to_be32(stag_index);
+
+       /*
+        * reference the request struct.  dereferenced in the int handler.
+        */
+       vq_req_get(c2dev, vq_req);
+
+       /*
+        * Send WR to adapter
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       /*
+        * Wait for reply from adapter
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail0;
+       }
+
+       /*
+        * Process reply
+        */
+       reply = (struct c2wr_stag_dealloc_rep *) (unsigned long) vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       err = c2_errno(reply);
+
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_mq.c b/drivers/infiniband/hw/amso1100/c2_mq.c
new file mode 100644 (file)
index 0000000..b88a755
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 "c2.h"
+#include "c2_mq.h"
+
+void *c2_mq_alloc(struct c2_mq *q)
+{
+       BUG_ON(q->magic != C2_MQ_MAGIC);
+       BUG_ON(q->type != C2_MQ_ADAPTER_TARGET);
+
+       if (c2_mq_full(q)) {
+               return NULL;
+       } else {
+#ifdef DEBUG
+               struct c2wr_hdr *m =
+                   (struct c2wr_hdr *) (q->msg_pool.host + q->priv * q->msg_size);
+#ifdef CCMSGMAGIC
+               BUG_ON(m->magic != be32_to_cpu(~CCWR_MAGIC));
+               m->magic = cpu_to_be32(CCWR_MAGIC);
+#endif
+               return m;
+#else
+               return q->msg_pool.host + q->priv * q->msg_size;
+#endif
+       }
+}
+
+void c2_mq_produce(struct c2_mq *q)
+{
+       BUG_ON(q->magic != C2_MQ_MAGIC);
+       BUG_ON(q->type != C2_MQ_ADAPTER_TARGET);
+
+       if (!c2_mq_full(q)) {
+               q->priv = (q->priv + 1) % q->q_size;
+               q->hint_count++;
+               /* Update peer's offset. */
+               __raw_writew(cpu_to_be16(q->priv), &q->peer->shared);
+       }
+}
+
+void *c2_mq_consume(struct c2_mq *q)
+{
+       BUG_ON(q->magic != C2_MQ_MAGIC);
+       BUG_ON(q->type != C2_MQ_HOST_TARGET);
+
+       if (c2_mq_empty(q)) {
+               return NULL;
+       } else {
+#ifdef DEBUG
+               struct c2wr_hdr *m = (struct c2wr_hdr *)
+                   (q->msg_pool.host + q->priv * q->msg_size);
+#ifdef CCMSGMAGIC
+               BUG_ON(m->magic != be32_to_cpu(CCWR_MAGIC));
+#endif
+               return m;
+#else
+               return q->msg_pool.host + q->priv * q->msg_size;
+#endif
+       }
+}
+
+void c2_mq_free(struct c2_mq *q)
+{
+       BUG_ON(q->magic != C2_MQ_MAGIC);
+       BUG_ON(q->type != C2_MQ_HOST_TARGET);
+
+       if (!c2_mq_empty(q)) {
+
+#ifdef CCMSGMAGIC
+               {
+                       struct c2wr_hdr __iomem *m = (struct c2wr_hdr __iomem *)
+                           (q->msg_pool.adapter + q->priv * q->msg_size);
+                       __raw_writel(cpu_to_be32(~CCWR_MAGIC), &m->magic);
+               }
+#endif
+               q->priv = (q->priv + 1) % q->q_size;
+               /* Update peer's offset. */
+               __raw_writew(cpu_to_be16(q->priv), &q->peer->shared);
+       }
+}
+
+
+void c2_mq_lconsume(struct c2_mq *q, u32 wqe_count)
+{
+       BUG_ON(q->magic != C2_MQ_MAGIC);
+       BUG_ON(q->type != C2_MQ_ADAPTER_TARGET);
+
+       while (wqe_count--) {
+               BUG_ON(c2_mq_empty(q));
+               *q->shared = cpu_to_be16((be16_to_cpu(*q->shared)+1) % q->q_size);
+       }
+}
+
+#if 0
+u32 c2_mq_count(struct c2_mq *q)
+{
+       s32 count;
+
+       if (q->type == C2_MQ_HOST_TARGET)
+               count = be16_to_cpu(*q->shared) - q->priv;
+       else
+               count = q->priv - be16_to_cpu(*q->shared);
+
+       if (count < 0)
+               count += q->q_size;
+
+       return (u32) count;
+}
+#endif  /*  0  */
+
+void c2_mq_req_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
+                   u8 __iomem *pool_start, u16 __iomem *peer, u32 type)
+{
+       BUG_ON(!q->shared);
+
+       /* This code assumes the byte swapping has already been done! */
+       q->index = index;
+       q->q_size = q_size;
+       q->msg_size = msg_size;
+       q->msg_pool.adapter = pool_start;
+       q->peer = (struct c2_mq_shared __iomem *) peer;
+       q->magic = C2_MQ_MAGIC;
+       q->type = type;
+       q->priv = 0;
+       q->hint_count = 0;
+       return;
+}
+void c2_mq_rep_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
+                   u8 *pool_start, u16 __iomem *peer, u32 type)
+{
+       BUG_ON(!q->shared);
+
+       /* This code assumes the byte swapping has already been done! */
+       q->index = index;
+       q->q_size = q_size;
+       q->msg_size = msg_size;
+       q->msg_pool.host = pool_start;
+       q->peer = (struct c2_mq_shared __iomem *) peer;
+       q->magic = C2_MQ_MAGIC;
+       q->type = type;
+       q->priv = 0;
+       q->hint_count = 0;
+       return;
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_mq.h b/drivers/infiniband/hw/amso1100/c2_mq.h
new file mode 100644 (file)
index 0000000..9185bbb
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 _C2_MQ_H_
+#define _C2_MQ_H_
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include "c2_wr.h"
+
+enum c2_shared_regs {
+
+       C2_SHARED_ARMED = 0x10,
+       C2_SHARED_NOTIFY = 0x18,
+       C2_SHARED_SHARED = 0x40,
+};
+
+struct c2_mq_shared {
+       u16 unused1;
+       u8 armed;
+       u8 notification_type;
+       u32 unused2;
+       u16 shared;
+       /* Pad to 64 bytes. */
+       u8 pad[64 - sizeof(u16) - 2 * sizeof(u8) - sizeof(u32) - sizeof(u16)];
+};
+
+enum c2_mq_type {
+       C2_MQ_HOST_TARGET = 1,
+       C2_MQ_ADAPTER_TARGET = 2,
+};
+
+/*
+ * c2_mq_t is for kernel-mode MQs like the VQs Cand the AEQ.
+ * c2_user_mq_t (which is the same format) is for user-mode MQs...
+ */
+#define C2_MQ_MAGIC 0x4d512020 /* 'MQ  ' */
+struct c2_mq {
+       u32 magic;
+       union {
+               u8 *host;
+               u8 __iomem *adapter;
+       } msg_pool;
+       dma_addr_t host_dma;
+       DECLARE_PCI_UNMAP_ADDR(mapping);
+       u16 hint_count;
+       u16 priv;
+       struct c2_mq_shared __iomem *peer;
+       u16 *shared;
+       dma_addr_t shared_dma;
+       u32 q_size;
+       u32 msg_size;
+       u32 index;
+       enum c2_mq_type type;
+};
+
+static __inline__ int c2_mq_empty(struct c2_mq *q)
+{
+       return q->priv == be16_to_cpu(*q->shared);
+}
+
+static __inline__ int c2_mq_full(struct c2_mq *q)
+{
+       return q->priv == (be16_to_cpu(*q->shared) + q->q_size - 1) % q->q_size;
+}
+
+extern void c2_mq_lconsume(struct c2_mq *q, u32 wqe_count);
+extern void *c2_mq_alloc(struct c2_mq *q);
+extern void c2_mq_produce(struct c2_mq *q);
+extern void *c2_mq_consume(struct c2_mq *q);
+extern void c2_mq_free(struct c2_mq *q);
+extern void c2_mq_req_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
+                      u8 __iomem *pool_start, u16 __iomem *peer, u32 type);
+extern void c2_mq_rep_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
+                          u8 *pool_start, u16 __iomem *peer, u32 type);
+
+#endif                         /* _C2_MQ_H_ */
diff --git a/drivers/infiniband/hw/amso1100/c2_pd.c b/drivers/infiniband/hw/amso1100/c2_pd.c
new file mode 100644 (file)
index 0000000..00c7099
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ * Copyright (c) 2005 Cisco Systems.  All rights reserved.
+ * Copyright (c) 2005 Mellanox Technologies. 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/init.h>
+#include <linux/errno.h>
+
+#include "c2.h"
+#include "c2_provider.h"
+
+int c2_pd_alloc(struct c2_dev *c2dev, int privileged, struct c2_pd *pd)
+{
+       u32 obj;
+       int ret = 0;
+
+       spin_lock(&c2dev->pd_table.lock);
+       obj = find_next_zero_bit(c2dev->pd_table.table, c2dev->pd_table.max,
+                                c2dev->pd_table.last);
+       if (obj >= c2dev->pd_table.max)
+               obj = find_first_zero_bit(c2dev->pd_table.table,
+                                         c2dev->pd_table.max);
+       if (obj < c2dev->pd_table.max) {
+               pd->pd_id = obj;
+               __set_bit(obj, c2dev->pd_table.table);
+               c2dev->pd_table.last = obj+1;
+               if (c2dev->pd_table.last >= c2dev->pd_table.max)
+                       c2dev->pd_table.last = 0;
+       } else
+               ret = -ENOMEM;
+       spin_unlock(&c2dev->pd_table.lock);
+       return ret;
+}
+
+void c2_pd_free(struct c2_dev *c2dev, struct c2_pd *pd)
+{
+       spin_lock(&c2dev->pd_table.lock);
+       __clear_bit(pd->pd_id, c2dev->pd_table.table);
+       spin_unlock(&c2dev->pd_table.lock);
+}
+
+int __devinit c2_init_pd_table(struct c2_dev *c2dev)
+{
+
+       c2dev->pd_table.last = 0;
+       c2dev->pd_table.max = c2dev->props.max_pd;
+       spin_lock_init(&c2dev->pd_table.lock);
+       c2dev->pd_table.table = kmalloc(BITS_TO_LONGS(c2dev->props.max_pd) *
+                                       sizeof(long), GFP_KERNEL);
+       if (!c2dev->pd_table.table)
+               return -ENOMEM;
+       bitmap_zero(c2dev->pd_table.table, c2dev->props.max_pd);
+       return 0;
+}
+
+void __devexit c2_cleanup_pd_table(struct c2_dev *c2dev)
+{
+       kfree(c2dev->pd_table.table);
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
new file mode 100644 (file)
index 0000000..8fddc8c
--- /dev/null
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/delay.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 <linux/dma-mapping.h>
+#include <linux/if_arp.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+#include <rdma/ib_smi.h>
+#include <rdma/ib_user_verbs.h>
+#include "c2.h"
+#include "c2_provider.h"
+#include "c2_user.h"
+
+static int c2_query_device(struct ib_device *ibdev,
+                          struct ib_device_attr *props)
+{
+       struct c2_dev *c2dev = to_c2dev(ibdev);
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       *props = c2dev->props;
+       return 0;
+}
+
+static int c2_query_port(struct ib_device *ibdev,
+                        u8 port, struct ib_port_attr *props)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       props->max_mtu = IB_MTU_4096;
+       props->lid = 0;
+       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 = 1;
+       props->active_speed = 1;
+
+       return 0;
+}
+
+static int c2_modify_port(struct ib_device *ibdev,
+                         u8 port, int port_modify_mask,
+                         struct ib_port_modify *props)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return 0;
+}
+
+static int c2_query_pkey(struct ib_device *ibdev,
+                        u8 port, u16 index, u16 * pkey)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       *pkey = 0;
+       return 0;
+}
+
+static int c2_query_gid(struct ib_device *ibdev, u8 port,
+                       int index, union ib_gid *gid)
+{
+       struct c2_dev *c2dev = to_c2dev(ibdev);
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       memset(&(gid->raw[0]), 0, sizeof(gid->raw));
+       memcpy(&(gid->raw[0]), c2dev->pseudo_netdev->dev_addr, 6);
+
+       return 0;
+}
+
+/* Allocate the user context data structure. This keeps track
+ * of all objects associated with a particular user-mode client.
+ */
+static struct ib_ucontext *c2_alloc_ucontext(struct ib_device *ibdev,
+                                            struct ib_udata *udata)
+{
+       struct c2_ucontext *context;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       context = kmalloc(sizeof(*context), GFP_KERNEL);
+       if (!context)
+               return ERR_PTR(-ENOMEM);
+
+       return &context->ibucontext;
+}
+
+static int c2_dealloc_ucontext(struct ib_ucontext *context)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       kfree(context);
+       return 0;
+}
+
+static int c2_mmap_uar(struct ib_ucontext *context, struct vm_area_struct *vma)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return -ENOSYS;
+}
+
+static struct ib_pd *c2_alloc_pd(struct ib_device *ibdev,
+                                struct ib_ucontext *context,
+                                struct ib_udata *udata)
+{
+       struct c2_pd *pd;
+       int err;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       pd = kmalloc(sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return ERR_PTR(-ENOMEM);
+
+       err = c2_pd_alloc(to_c2dev(ibdev), !context, pd);
+       if (err) {
+               kfree(pd);
+               return ERR_PTR(err);
+       }
+
+       if (context) {
+               if (ib_copy_to_udata(udata, &pd->pd_id, sizeof(__u32))) {
+                       c2_pd_free(to_c2dev(ibdev), pd);
+                       kfree(pd);
+                       return ERR_PTR(-EFAULT);
+               }
+       }
+
+       return &pd->ibpd;
+}
+
+static int c2_dealloc_pd(struct ib_pd *pd)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       c2_pd_free(to_c2dev(pd->device), to_c2pd(pd));
+       kfree(pd);
+
+       return 0;
+}
+
+static struct ib_ah *c2_ah_create(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return ERR_PTR(-ENOSYS);
+}
+
+static int c2_ah_destroy(struct ib_ah *ah)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return -ENOSYS;
+}
+
+static void c2_add_ref(struct ib_qp *ibqp)
+{
+       struct c2_qp *qp;
+       BUG_ON(!ibqp);
+       qp = to_c2qp(ibqp);
+       atomic_inc(&qp->refcount);
+}
+
+static void c2_rem_ref(struct ib_qp *ibqp)
+{
+       struct c2_qp *qp;
+       BUG_ON(!ibqp);
+       qp = to_c2qp(ibqp);
+       if (atomic_dec_and_test(&qp->refcount))
+               wake_up(&qp->wait);
+}
+
+struct ib_qp *c2_get_qp(struct ib_device *device, int qpn)
+{
+       struct c2_dev* c2dev = to_c2dev(device);
+       struct c2_qp *qp;
+
+       qp = c2_find_qpn(c2dev, qpn);
+       pr_debug("%s Returning QP=%p for QPN=%d, device=%p, refcount=%d\n",
+               __FUNCTION__, qp, qpn, device,
+               (qp?atomic_read(&qp->refcount):0));
+
+       return (qp?&qp->ibqp:NULL);
+}
+
+static struct ib_qp *c2_create_qp(struct ib_pd *pd,
+                                 struct ib_qp_init_attr *init_attr,
+                                 struct ib_udata *udata)
+{
+       struct c2_qp *qp;
+       int err;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       switch (init_attr->qp_type) {
+       case IB_QPT_RC:
+               qp = kzalloc(sizeof(*qp), GFP_KERNEL);
+               if (!qp) {
+                       pr_debug("%s: Unable to allocate QP\n", __FUNCTION__);
+                       return ERR_PTR(-ENOMEM);
+               }
+               spin_lock_init(&qp->lock);
+               if (pd->uobject) {
+                       /* userspace specific */
+               }
+
+               err = c2_alloc_qp(to_c2dev(pd->device),
+                                 to_c2pd(pd), init_attr, qp);
+
+               if (err && pd->uobject) {
+                       /* userspace specific */
+               }
+
+               break;
+       default:
+               pr_debug("%s: Invalid QP type: %d\n", __FUNCTION__,
+                       init_attr->qp_type);
+               return ERR_PTR(-EINVAL);
+               break;
+       }
+
+       if (err) {
+               kfree(qp);
+               return ERR_PTR(err);
+       }
+
+       return &qp->ibqp;
+}
+
+static int c2_destroy_qp(struct ib_qp *ib_qp)
+{
+       struct c2_qp *qp = to_c2qp(ib_qp);
+
+       pr_debug("%s:%u qp=%p,qp->state=%d\n",
+               __FUNCTION__, __LINE__,ib_qp,qp->state);
+       c2_free_qp(to_c2dev(ib_qp->device), qp);
+       kfree(qp);
+       return 0;
+}
+
+static struct ib_cq *c2_create_cq(struct ib_device *ibdev, int entries,
+                                 struct ib_ucontext *context,
+                                 struct ib_udata *udata)
+{
+       struct c2_cq *cq;
+       int err;
+
+       cq = kmalloc(sizeof(*cq), GFP_KERNEL);
+       if (!cq) {
+               pr_debug("%s: Unable to allocate CQ\n", __FUNCTION__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       err = c2_init_cq(to_c2dev(ibdev), entries, NULL, cq);
+       if (err) {
+               pr_debug("%s: error initializing CQ\n", __FUNCTION__);
+               kfree(cq);
+               return ERR_PTR(err);
+       }
+
+       return &cq->ibcq;
+}
+
+static int c2_destroy_cq(struct ib_cq *ib_cq)
+{
+       struct c2_cq *cq = to_c2cq(ib_cq);
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       c2_free_cq(to_c2dev(ib_cq->device), cq);
+       kfree(cq);
+
+       return 0;
+}
+
+static inline u32 c2_convert_access(int acc)
+{
+       return (acc & IB_ACCESS_REMOTE_WRITE ? C2_ACF_REMOTE_WRITE : 0) |
+           (acc & IB_ACCESS_REMOTE_READ ? C2_ACF_REMOTE_READ : 0) |
+           (acc & IB_ACCESS_LOCAL_WRITE ? C2_ACF_LOCAL_WRITE : 0) |
+           C2_ACF_LOCAL_READ | C2_ACF_WINDOW_BIND;
+}
+
+static struct ib_mr *c2_reg_phys_mr(struct ib_pd *ib_pd,
+                                   struct ib_phys_buf *buffer_list,
+                                   int num_phys_buf, int acc, u64 * iova_start)
+{
+       struct c2_mr *mr;
+       u64 *page_list;
+       u32 total_len;
+       int err, i, j, k, page_shift, pbl_depth;
+
+       pbl_depth = 0;
+       total_len = 0;
+
+       page_shift = PAGE_SHIFT;
+       /*
+        * If there is only 1 buffer we assume this could
+        * be a map of all phy mem...use a 32k page_shift.
+        */
+       if (num_phys_buf == 1)
+               page_shift += 3;
+
+       for (i = 0; i < num_phys_buf; i++) {
+
+               if (buffer_list[i].addr & ~PAGE_MASK) {
+                       pr_debug("Unaligned Memory Buffer: 0x%x\n",
+                               (unsigned int) buffer_list[i].addr);
+                       return ERR_PTR(-EINVAL);
+               }
+
+               if (!buffer_list[i].size) {
+                       pr_debug("Invalid Buffer Size\n");
+                       return ERR_PTR(-EINVAL);
+               }
+
+               total_len += buffer_list[i].size;
+               pbl_depth += ALIGN(buffer_list[i].size,
+                                  (1 << page_shift)) >> page_shift;
+       }
+
+       page_list = vmalloc(sizeof(u64) * pbl_depth);
+       if (!page_list) {
+               pr_debug("couldn't vmalloc page_list of size %zd\n",
+                       (sizeof(u64) * pbl_depth));
+               return ERR_PTR(-ENOMEM);
+       }
+
+       for (i = 0, j = 0; i < num_phys_buf; i++) {
+
+               int naddrs;
+
+               naddrs = ALIGN(buffer_list[i].size,
+                              (1 << page_shift)) >> page_shift;
+               for (k = 0; k < naddrs; k++)
+                       page_list[j++] = (buffer_list[i].addr +
+                                                    (k << page_shift));
+       }
+
+       mr = kmalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(-ENOMEM);
+
+       mr->pd = to_c2pd(ib_pd);
+       pr_debug("%s - page shift %d, pbl_depth %d, total_len %u, "
+               "*iova_start %llx, first pa %llx, last pa %llx\n",
+               __FUNCTION__, page_shift, pbl_depth, total_len,
+               *iova_start, page_list[0], page_list[pbl_depth-1]);
+       err = c2_nsmr_register_phys_kern(to_c2dev(ib_pd->device), page_list,
+                                        (1 << page_shift), pbl_depth,
+                                        total_len, 0, iova_start,
+                                        c2_convert_access(acc), mr);
+       vfree(page_list);
+       if (err) {
+               kfree(mr);
+               return ERR_PTR(err);
+       }
+
+       return &mr->ibmr;
+}
+
+static struct ib_mr *c2_get_dma_mr(struct ib_pd *pd, int acc)
+{
+       struct ib_phys_buf bl;
+       u64 kva = 0;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       /* AMSO1100 limit */
+       bl.size = 0xffffffff;
+       bl.addr = 0;
+       return c2_reg_phys_mr(pd, &bl, 1, acc, &kva);
+}
+
+static struct ib_mr *c2_reg_user_mr(struct ib_pd *pd, struct ib_umem *region,
+                                   int acc, struct ib_udata *udata)
+{
+       u64 *pages;
+       u64 kva = 0;
+       int shift, n, len;
+       int i, j, k;
+       int err = 0;
+       struct ib_umem_chunk *chunk;
+       struct c2_pd *c2pd = to_c2pd(pd);
+       struct c2_mr *c2mr;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       shift = ffs(region->page_size) - 1;
+
+       c2mr = kmalloc(sizeof(*c2mr), GFP_KERNEL);
+       if (!c2mr)
+               return ERR_PTR(-ENOMEM);
+       c2mr->pd = c2pd;
+
+       n = 0;
+       list_for_each_entry(chunk, &region->chunk_list, list)
+               n += chunk->nents;
+
+       pages = kmalloc(n * sizeof(u64), GFP_KERNEL);
+       if (!pages) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       i = 0;
+       list_for_each_entry(chunk, &region->chunk_list, list) {
+               for (j = 0; j < chunk->nmap; ++j) {
+                       len = sg_dma_len(&chunk->page_list[j]) >> shift;
+                       for (k = 0; k < len; ++k) {
+                               pages[i++] =
+                                       sg_dma_address(&chunk->page_list[j]) +
+                                       (region->page_size * k);
+                       }
+               }
+       }
+
+       kva = (u64)region->virt_base;
+       err = c2_nsmr_register_phys_kern(to_c2dev(pd->device),
+                                        pages,
+                                        region->page_size,
+                                        i,
+                                        region->length,
+                                        region->offset,
+                                        &kva,
+                                        c2_convert_access(acc),
+                                        c2mr);
+       kfree(pages);
+       if (err) {
+               kfree(c2mr);
+               return ERR_PTR(err);
+       }
+       return &c2mr->ibmr;
+
+err:
+       kfree(c2mr);
+       return ERR_PTR(err);
+}
+
+static int c2_dereg_mr(struct ib_mr *ib_mr)
+{
+       struct c2_mr *mr = to_c2mr(ib_mr);
+       int err;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       err = c2_stag_dealloc(to_c2dev(ib_mr->device), ib_mr->lkey);
+       if (err)
+               pr_debug("c2_stag_dealloc failed: %d\n", err);
+       else
+               kfree(mr);
+
+       return err;
+}
+
+static ssize_t show_rev(struct class_device *cdev, char *buf)
+{
+       struct c2_dev *dev = container_of(cdev, struct c2_dev, ibdev.class_dev);
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return sprintf(buf, "%x\n", dev->props.hw_ver);
+}
+
+static ssize_t show_fw_ver(struct class_device *cdev, char *buf)
+{
+       struct c2_dev *dev = container_of(cdev, struct c2_dev, ibdev.class_dev);
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return sprintf(buf, "%x.%x.%x\n",
+                      (int) (dev->props.fw_ver >> 32),
+                      (int) (dev->props.fw_ver >> 16) & 0xffff,
+                      (int) (dev->props.fw_ver & 0xffff));
+}
+
+static ssize_t show_hca(struct class_device *cdev, char *buf)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return sprintf(buf, "AMSO1100\n");
+}
+
+static ssize_t show_board(struct class_device *cdev, char *buf)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return sprintf(buf, "%.*s\n", 32, "AMSO1100 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 *c2_class_attributes[] = {
+       &class_device_attr_hw_rev,
+       &class_device_attr_fw_ver,
+       &class_device_attr_hca_type,
+       &class_device_attr_board_id
+};
+
+static int c2_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                       int attr_mask, struct ib_udata *udata)
+{
+       int err;
+
+       err =
+           c2_qp_modify(to_c2dev(ibqp->device), to_c2qp(ibqp), attr,
+                        attr_mask);
+
+       return err;
+}
+
+static int c2_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return -ENOSYS;
+}
+
+static int c2_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return -ENOSYS;
+}
+
+static int c2_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)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return -ENOSYS;
+}
+
+static int c2_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       /* Request a connection */
+       return c2_llp_connect(cm_id, iw_param);
+}
+
+static int c2_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       /* Accept the new connection */
+       return c2_llp_accept(cm_id, iw_param);
+}
+
+static int c2_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
+{
+       int err;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       err = c2_llp_reject(cm_id, pdata, pdata_len);
+       return err;
+}
+
+static int c2_service_create(struct iw_cm_id *cm_id, int backlog)
+{
+       int err;
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       err = c2_llp_service_create(cm_id, backlog);
+       pr_debug("%s:%u err=%d\n",
+               __FUNCTION__, __LINE__,
+               err);
+       return err;
+}
+
+static int c2_service_destroy(struct iw_cm_id *cm_id)
+{
+       int err;
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+
+       err = c2_llp_service_destroy(cm_id);
+
+       return err;
+}
+
+static int c2_pseudo_up(struct net_device *netdev)
+{
+       struct in_device *ind;
+       struct c2_dev *c2dev = netdev->priv;
+
+       ind = in_dev_get(netdev);
+       if (!ind)
+               return 0;
+
+       pr_debug("adding...\n");
+       for_ifa(ind) {
+#ifdef DEBUG
+               u8 *ip = (u8 *) & ifa->ifa_address;
+
+               pr_debug("%s: %d.%d.%d.%d\n",
+                      ifa->ifa_label, ip[0], ip[1], ip[2], ip[3]);
+#endif
+               c2_add_addr(c2dev, ifa->ifa_address, ifa->ifa_mask);
+       }
+       endfor_ifa(ind);
+       in_dev_put(ind);
+
+       return 0;
+}
+
+static int c2_pseudo_down(struct net_device *netdev)
+{
+       struct in_device *ind;
+       struct c2_dev *c2dev = netdev->priv;
+
+       ind = in_dev_get(netdev);
+       if (!ind)
+               return 0;
+
+       pr_debug("deleting...\n");
+       for_ifa(ind) {
+#ifdef DEBUG
+               u8 *ip = (u8 *) & ifa->ifa_address;
+
+               pr_debug("%s: %d.%d.%d.%d\n",
+                      ifa->ifa_label, ip[0], ip[1], ip[2], ip[3]);
+#endif
+               c2_del_addr(c2dev, ifa->ifa_address, ifa->ifa_mask);
+       }
+       endfor_ifa(ind);
+       in_dev_put(ind);
+
+       return 0;
+}
+
+static int c2_pseudo_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+       kfree_skb(skb);
+       return NETDEV_TX_OK;
+}
+
+static int c2_pseudo_change_mtu(struct net_device *netdev, int new_mtu)
+{
+       int ret = 0;
+
+       if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
+               return -EINVAL;
+
+       netdev->mtu = new_mtu;
+
+       /* TODO: Tell rnic about new rmda interface mtu */
+       return ret;
+}
+
+static void setup(struct net_device *netdev)
+{
+       SET_MODULE_OWNER(netdev);
+       netdev->open = c2_pseudo_up;
+       netdev->stop = c2_pseudo_down;
+       netdev->hard_start_xmit = c2_pseudo_xmit_frame;
+       netdev->get_stats = NULL;
+       netdev->tx_timeout = NULL;
+       netdev->set_mac_address = NULL;
+       netdev->change_mtu = c2_pseudo_change_mtu;
+       netdev->watchdog_timeo = 0;
+       netdev->type = ARPHRD_ETHER;
+       netdev->mtu = 1500;
+       netdev->hard_header_len = ETH_HLEN;
+       netdev->addr_len = ETH_ALEN;
+       netdev->tx_queue_len = 0;
+       netdev->flags |= IFF_NOARP;
+       return;
+}
+
+static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev)
+{
+       char name[IFNAMSIZ];
+       struct net_device *netdev;
+
+       /* change ethxxx to iwxxx */
+       strcpy(name, "iw");
+       strcat(name, &c2dev->netdev->name[3]);
+       netdev = alloc_netdev(sizeof(*netdev), name, setup);
+       if (!netdev) {
+               printk(KERN_ERR PFX "%s -  etherdev alloc failed",
+                       __FUNCTION__);
+               return NULL;
+       }
+
+       netdev->priv = c2dev;
+
+       SET_NETDEV_DEV(netdev, &c2dev->pcidev->dev);
+
+       memcpy_fromio(netdev->dev_addr, c2dev->kva + C2_REGS_RDMA_ENADDR, 6);
+
+       /* Print out the MAC address */
+       pr_debug("%s: MAC %02X:%02X:%02X:%02X:%02X:%02X\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]);
+
+#if 0
+       /* Disable network packets */
+       netif_stop_queue(netdev);
+#endif
+       return netdev;
+}
+
+int c2_register_device(struct c2_dev *dev)
+{
+       int ret;
+       int i;
+
+       /* Register pseudo network device */
+       dev->pseudo_netdev = c2_pseudo_netdev_init(dev);
+       if (dev->pseudo_netdev) {
+               ret = register_netdev(dev->pseudo_netdev);
+               if (ret) {
+                       printk(KERN_ERR PFX
+                               "Unable to register netdev, ret = %d\n", ret);
+                       free_netdev(dev->pseudo_netdev);
+                       return ret;
+               }
+       }
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       strlcpy(dev->ibdev.name, "amso%d", IB_DEVICE_NAME_MAX);
+       dev->ibdev.owner = THIS_MODULE;
+       dev->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_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_POST_SEND) |
+           (1ull << IB_USER_VERBS_CMD_POST_RECV);
+
+       dev->ibdev.node_type = RDMA_NODE_RNIC;
+       memset(&dev->ibdev.node_guid, 0, sizeof(dev->ibdev.node_guid));
+       memcpy(&dev->ibdev.node_guid, dev->pseudo_netdev->dev_addr, 6);
+       dev->ibdev.phys_port_cnt = 1;
+       dev->ibdev.dma_device = &dev->pcidev->dev;
+       dev->ibdev.class_dev.dev = &dev->pcidev->dev;
+       dev->ibdev.query_device = c2_query_device;
+       dev->ibdev.query_port = c2_query_port;
+       dev->ibdev.modify_port = c2_modify_port;
+       dev->ibdev.query_pkey = c2_query_pkey;
+       dev->ibdev.query_gid = c2_query_gid;
+       dev->ibdev.alloc_ucontext = c2_alloc_ucontext;
+       dev->ibdev.dealloc_ucontext = c2_dealloc_ucontext;
+       dev->ibdev.mmap = c2_mmap_uar;
+       dev->ibdev.alloc_pd = c2_alloc_pd;
+       dev->ibdev.dealloc_pd = c2_dealloc_pd;
+       dev->ibdev.create_ah = c2_ah_create;
+       dev->ibdev.destroy_ah = c2_ah_destroy;
+       dev->ibdev.create_qp = c2_create_qp;
+       dev->ibdev.modify_qp = c2_modify_qp;
+       dev->ibdev.destroy_qp = c2_destroy_qp;
+       dev->ibdev.create_cq = c2_create_cq;
+       dev->ibdev.destroy_cq = c2_destroy_cq;
+       dev->ibdev.poll_cq = c2_poll_cq;
+       dev->ibdev.get_dma_mr = c2_get_dma_mr;
+       dev->ibdev.reg_phys_mr = c2_reg_phys_mr;
+       dev->ibdev.reg_user_mr = c2_reg_user_mr;
+       dev->ibdev.dereg_mr = c2_dereg_mr;
+
+       dev->ibdev.alloc_fmr = NULL;
+       dev->ibdev.unmap_fmr = NULL;
+       dev->ibdev.dealloc_fmr = NULL;
+       dev->ibdev.map_phys_fmr = NULL;
+
+       dev->ibdev.attach_mcast = c2_multicast_attach;
+       dev->ibdev.detach_mcast = c2_multicast_detach;
+       dev->ibdev.process_mad = c2_process_mad;
+
+       dev->ibdev.req_notify_cq = c2_arm_cq;
+       dev->ibdev.post_send = c2_post_send;
+       dev->ibdev.post_recv = c2_post_receive;
+
+       dev->ibdev.iwcm = kmalloc(sizeof(*dev->ibdev.iwcm), GFP_KERNEL);
+       dev->ibdev.iwcm->add_ref = c2_add_ref;
+       dev->ibdev.iwcm->rem_ref = c2_rem_ref;
+       dev->ibdev.iwcm->get_qp = c2_get_qp;
+       dev->ibdev.iwcm->connect = c2_connect;
+       dev->ibdev.iwcm->accept = c2_accept;
+       dev->ibdev.iwcm->reject = c2_reject;
+       dev->ibdev.iwcm->create_listen = c2_service_create;
+       dev->ibdev.iwcm->destroy_listen = c2_service_destroy;
+
+       ret = ib_register_device(&dev->ibdev);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(c2_class_attributes); ++i) {
+               ret = class_device_create_file(&dev->ibdev.class_dev,
+                                              c2_class_attributes[i]);
+               if (ret) {
+                       unregister_netdev(dev->pseudo_netdev);
+                       free_netdev(dev->pseudo_netdev);
+                       ib_unregister_device(&dev->ibdev);
+                       return ret;
+               }
+       }
+
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       return 0;
+}
+
+void c2_unregister_device(struct c2_dev *dev)
+{
+       pr_debug("%s:%u\n", __FUNCTION__, __LINE__);
+       unregister_netdev(dev->pseudo_netdev);
+       free_netdev(dev->pseudo_netdev);
+       ib_unregister_device(&dev->ibdev);
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.h b/drivers/infiniband/hw/amso1100/c2_provider.h
new file mode 100644 (file)
index 0000000..fc90622
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 C2_PROVIDER_H
+#define C2_PROVIDER_H
+#include <linux/inetdevice.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_pack.h>
+
+#include "c2_mq.h"
+#include <rdma/iw_cm.h>
+
+#define C2_MPT_FLAG_ATOMIC        (1 << 14)
+#define C2_MPT_FLAG_REMOTE_WRITE  (1 << 13)
+#define C2_MPT_FLAG_REMOTE_READ   (1 << 12)
+#define C2_MPT_FLAG_LOCAL_WRITE   (1 << 11)
+#define C2_MPT_FLAG_LOCAL_READ    (1 << 10)
+
+struct c2_buf_list {
+       void *buf;
+        DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+
+/* The user context keeps track of objects allocated for a
+ * particular user-mode client. */
+struct c2_ucontext {
+       struct ib_ucontext ibucontext;
+};
+
+struct c2_mtt;
+
+/* All objects associated with a PD are kept in the
+ * associated user context if present.
+ */
+struct c2_pd {
+       struct ib_pd ibpd;
+       u32 pd_id;
+};
+
+struct c2_mr {
+       struct ib_mr ibmr;
+       struct c2_pd *pd;
+};
+
+struct c2_av;
+
+enum c2_ah_type {
+       C2_AH_ON_HCA,
+       C2_AH_PCI_POOL,
+       C2_AH_KMALLOC
+};
+
+struct c2_ah {
+       struct ib_ah ibah;
+};
+
+struct c2_cq {
+       struct ib_cq ibcq;
+       spinlock_t lock;
+       atomic_t refcount;
+       int cqn;
+       int is_kernel;
+       wait_queue_head_t wait;
+
+       u32 adapter_handle;
+       struct c2_mq mq;
+};
+
+struct c2_wq {
+       spinlock_t lock;
+};
+struct iw_cm_id;
+struct c2_qp {
+       struct ib_qp ibqp;
+       struct iw_cm_id *cm_id;
+       spinlock_t lock;
+       atomic_t refcount;
+       wait_queue_head_t wait;
+       int qpn;
+
+       u32 adapter_handle;
+       u32 send_sgl_depth;
+       u32 recv_sgl_depth;
+       u32 rdma_write_sgl_depth;
+       u8 state;
+
+       struct c2_mq sq_mq;
+       struct c2_mq rq_mq;
+};
+
+struct c2_cr_query_attrs {
+       u32 local_addr;
+       u32 remote_addr;
+       u16 local_port;
+       u16 remote_port;
+};
+
+static inline struct c2_pd *to_c2pd(struct ib_pd *ibpd)
+{
+       return container_of(ibpd, struct c2_pd, ibpd);
+}
+
+static inline struct c2_ucontext *to_c2ucontext(struct ib_ucontext *ibucontext)
+{
+       return container_of(ibucontext, struct c2_ucontext, ibucontext);
+}
+
+static inline struct c2_mr *to_c2mr(struct ib_mr *ibmr)
+{
+       return container_of(ibmr, struct c2_mr, ibmr);
+}
+
+
+static inline struct c2_ah *to_c2ah(struct ib_ah *ibah)
+{
+       return container_of(ibah, struct c2_ah, ibah);
+}
+
+static inline struct c2_cq *to_c2cq(struct ib_cq *ibcq)
+{
+       return container_of(ibcq, struct c2_cq, ibcq);
+}
+
+static inline struct c2_qp *to_c2qp(struct ib_qp *ibqp)
+{
+       return container_of(ibqp, struct c2_qp, ibqp);
+}
+
+static inline int is_rnic_addr(struct net_device *netdev, u32 addr)
+{
+       struct in_device *ind;
+       int ret = 0;
+
+       ind = in_dev_get(netdev);
+       if (!ind)
+               return 0;
+
+       for_ifa(ind) {
+               if (ifa->ifa_address == addr) {
+                       ret = 1;
+                       break;
+               }
+       }
+       endfor_ifa(ind);
+       in_dev_put(ind);
+       return ret;
+}
+#endif                         /* C2_PROVIDER_H */
diff --git a/drivers/infiniband/hw/amso1100/c2_qp.c b/drivers/infiniband/hw/amso1100/c2_qp.c
new file mode 100644 (file)
index 0000000..1226113
--- /dev/null
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ * Copyright (c) 2005 Cisco Systems. All rights reserved.
+ * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2004 Voltaire, 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 "c2.h"
+#include "c2_vq.h"
+#include "c2_status.h"
+
+#define C2_MAX_ORD_PER_QP 128
+#define C2_MAX_IRD_PER_QP 128
+
+#define C2_HINT_MAKE(q_index, hint_count) (((q_index) << 16) | hint_count)
+#define C2_HINT_GET_INDEX(hint) (((hint) & 0x7FFF0000) >> 16)
+#define C2_HINT_GET_COUNT(hint) ((hint) & 0x0000FFFF)
+
+#define NO_SUPPORT -1
+static const u8 c2_opcode[] = {
+       [IB_WR_SEND] = C2_WR_TYPE_SEND,
+       [IB_WR_SEND_WITH_IMM] = NO_SUPPORT,
+       [IB_WR_RDMA_WRITE] = C2_WR_TYPE_RDMA_WRITE,
+       [IB_WR_RDMA_WRITE_WITH_IMM] = NO_SUPPORT,
+       [IB_WR_RDMA_READ] = C2_WR_TYPE_RDMA_READ,
+       [IB_WR_ATOMIC_CMP_AND_SWP] = NO_SUPPORT,
+       [IB_WR_ATOMIC_FETCH_AND_ADD] = NO_SUPPORT,
+};
+
+static int to_c2_state(enum ib_qp_state ib_state)
+{
+       switch (ib_state) {
+       case IB_QPS_RESET:
+               return C2_QP_STATE_IDLE;
+       case IB_QPS_RTS:
+               return C2_QP_STATE_RTS;
+       case IB_QPS_SQD:
+               return C2_QP_STATE_CLOSING;
+       case IB_QPS_SQE:
+               return C2_QP_STATE_CLOSING;
+       case IB_QPS_ERR:
+               return C2_QP_STATE_ERROR;
+       default:
+               return -1;
+       }
+}
+
+static int to_ib_state(enum c2_qp_state c2_state)
+{
+       switch (c2_state) {
+       case C2_QP_STATE_IDLE:
+               return IB_QPS_RESET;
+       case C2_QP_STATE_CONNECTING:
+               return IB_QPS_RTR;
+       case C2_QP_STATE_RTS:
+               return IB_QPS_RTS;
+       case C2_QP_STATE_CLOSING:
+               return IB_QPS_SQD;
+       case C2_QP_STATE_ERROR:
+               return IB_QPS_ERR;
+       case C2_QP_STATE_TERMINATE:
+               return IB_QPS_SQE;
+       default:
+               return -1;
+       }
+}
+
+static const char *to_ib_state_str(int ib_state)
+{
+       static const char *state_str[] = {
+               "IB_QPS_RESET",
+               "IB_QPS_INIT",
+               "IB_QPS_RTR",
+               "IB_QPS_RTS",
+               "IB_QPS_SQD",
+               "IB_QPS_SQE",
+               "IB_QPS_ERR"
+       };
+       if (ib_state < IB_QPS_RESET ||
+           ib_state > IB_QPS_ERR)
+               return "<invalid IB QP state>";
+
+       ib_state -= IB_QPS_RESET;
+       return state_str[ib_state];
+}
+
+void c2_set_qp_state(struct c2_qp *qp, int c2_state)
+{
+       int new_state = to_ib_state(c2_state);
+
+       pr_debug("%s: qp[%p] state modify %s --> %s\n",
+              __FUNCTION__,
+               qp,
+               to_ib_state_str(qp->state),
+               to_ib_state_str(new_state));
+       qp->state = new_state;
+}
+
+#define C2_QP_NO_ATTR_CHANGE 0xFFFFFFFF
+
+int c2_qp_modify(struct c2_dev *c2dev, struct c2_qp *qp,
+                struct ib_qp_attr *attr, int attr_mask)
+{
+       struct c2wr_qp_modify_req wr;
+       struct c2wr_qp_modify_rep *reply;
+       struct c2_vq_req *vq_req;
+       unsigned long flags;
+       u8 next_state;
+       int err;
+
+       pr_debug("%s:%d qp=%p, %s --> %s\n",
+               __FUNCTION__, __LINE__,
+               qp,
+               to_ib_state_str(qp->state),
+               to_ib_state_str(attr->qp_state));
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       c2_wr_set_id(&wr, CCWR_QP_MODIFY);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.qp_handle = qp->adapter_handle;
+       wr.ord = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+       wr.ird = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+       wr.sq_depth = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+       wr.rq_depth = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+
+       if (attr_mask & IB_QP_STATE) {
+               /* Ensure the state is valid */
+               if (attr->qp_state < 0 || attr->qp_state > IB_QPS_ERR)
+                       return -EINVAL;
+
+               wr.next_qp_state = cpu_to_be32(to_c2_state(attr->qp_state));
+
+               if (attr->qp_state == IB_QPS_ERR) {
+                       spin_lock_irqsave(&qp->lock, flags);
+                       if (qp->cm_id && qp->state == IB_QPS_RTS) {
+                               pr_debug("Generating CLOSE event for QP-->ERR, "
+                                       "qp=%p, cm_id=%p\n",qp,qp->cm_id);
+                               /* Generate an CLOSE event */
+                               vq_req->cm_id = qp->cm_id;
+                               vq_req->event = IW_CM_EVENT_CLOSE;
+                       }
+                       spin_unlock_irqrestore(&qp->lock, flags);
+               }
+               next_state =  attr->qp_state;
+
+       } else if (attr_mask & IB_QP_CUR_STATE) {
+
+               if (attr->cur_qp_state != IB_QPS_RTR &&
+                   attr->cur_qp_state != IB_QPS_RTS &&
+                   attr->cur_qp_state != IB_QPS_SQD &&
+                   attr->cur_qp_state != IB_QPS_SQE)
+                       return -EINVAL;
+               else
+                       wr.next_qp_state =
+                           cpu_to_be32(to_c2_state(attr->cur_qp_state));
+
+               next_state = attr->cur_qp_state;
+
+       } else {
+               err = 0;
+               goto bail0;
+       }
+
+       /* reference the request struct */
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail0;
+
+       reply = (struct c2wr_qp_modify_rep *) (unsigned long) vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       err = c2_errno(reply);
+       if (!err)
+               qp->state = next_state;
+#ifdef DEBUG
+       else
+               pr_debug("%s: c2_errno=%d\n", __FUNCTION__, err);
+#endif
+       /*
+        * If we're going to error and generating the event here, then
+        * we need to remove the reference because there will be no
+        * close event generated by the adapter
+       */
+       spin_lock_irqsave(&qp->lock, flags);
+       if (vq_req->event==IW_CM_EVENT_CLOSE && qp->cm_id) {
+               qp->cm_id->rem_ref(qp->cm_id);
+               qp->cm_id = NULL;
+       }
+       spin_unlock_irqrestore(&qp->lock, flags);
+
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+
+       pr_debug("%s:%d qp=%p, cur_state=%s\n",
+               __FUNCTION__, __LINE__,
+               qp,
+               to_ib_state_str(qp->state));
+       return err;
+}
+
+int c2_qp_set_read_limits(struct c2_dev *c2dev, struct c2_qp *qp,
+                         int ord, int ird)
+{
+       struct c2wr_qp_modify_req wr;
+       struct c2wr_qp_modify_rep *reply;
+       struct c2_vq_req *vq_req;
+       int err;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       c2_wr_set_id(&wr, CCWR_QP_MODIFY);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.qp_handle = qp->adapter_handle;
+       wr.ord = cpu_to_be32(ord);
+       wr.ird = cpu_to_be32(ird);
+       wr.sq_depth = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+       wr.rq_depth = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+       wr.next_qp_state = cpu_to_be32(C2_QP_NO_ATTR_CHANGE);
+
+       /* reference the request struct */
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail0;
+
+       reply = (struct c2wr_qp_modify_rep *) (unsigned long)
+               vq_req->reply_msg;
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       err = c2_errno(reply);
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+static int destroy_qp(struct c2_dev *c2dev, struct c2_qp *qp)
+{
+       struct c2_vq_req *vq_req;
+       struct c2wr_qp_destroy_req wr;
+       struct c2wr_qp_destroy_rep *reply;
+       unsigned long flags;
+       int err;
+
+       /*
+        * Allocate a verb request message
+        */
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req) {
+               return -ENOMEM;
+       }
+
+       /*
+        * Initialize the WR
+        */
+       c2_wr_set_id(&wr, CCWR_QP_DESTROY);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.qp_handle = qp->adapter_handle;
+
+       /*
+        * reference the request struct.  dereferenced in the int handler.
+        */
+       vq_req_get(c2dev, vq_req);
+
+       spin_lock_irqsave(&qp->lock, flags);
+       if (qp->cm_id && qp->state == IB_QPS_RTS) {
+               pr_debug("destroy_qp: generating CLOSE event for QP-->ERR, "
+                       "qp=%p, cm_id=%p\n",qp,qp->cm_id);
+               /* Generate an CLOSE event */
+               vq_req->qp = qp;
+               vq_req->cm_id = qp->cm_id;
+               vq_req->event = IW_CM_EVENT_CLOSE;
+       }
+       spin_unlock_irqrestore(&qp->lock, flags);
+
+       /*
+        * Send WR to adapter
+        */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       /*
+        * Wait for reply from adapter
+        */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail0;
+       }
+
+       /*
+        * Process reply
+        */
+       reply = (struct c2wr_qp_destroy_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       spin_lock_irqsave(&qp->lock, flags);
+       if (qp->cm_id) {
+               qp->cm_id->rem_ref(qp->cm_id);
+               qp->cm_id = NULL;
+       }
+       spin_unlock_irqrestore(&qp->lock, flags);
+
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+static int c2_alloc_qpn(struct c2_dev *c2dev, struct c2_qp *qp)
+{
+       int ret;
+
+        do {
+               spin_lock_irq(&c2dev->qp_table.lock);
+               ret = idr_get_new_above(&c2dev->qp_table.idr, qp,
+                                       c2dev->qp_table.last++, &qp->qpn);
+               spin_unlock_irq(&c2dev->qp_table.lock);
+        } while ((ret == -EAGAIN) &&
+                idr_pre_get(&c2dev->qp_table.idr, GFP_KERNEL));
+       return ret;
+}
+
+static void c2_free_qpn(struct c2_dev *c2dev, int qpn)
+{
+       spin_lock_irq(&c2dev->qp_table.lock);
+       idr_remove(&c2dev->qp_table.idr, qpn);
+       spin_unlock_irq(&c2dev->qp_table.lock);
+}
+
+struct c2_qp *c2_find_qpn(struct c2_dev *c2dev, int qpn)
+{
+       unsigned long flags;
+       struct c2_qp *qp;
+
+       spin_lock_irqsave(&c2dev->qp_table.lock, flags);
+       qp = idr_find(&c2dev->qp_table.idr, qpn);
+       spin_unlock_irqrestore(&c2dev->qp_table.lock, flags);
+       return qp;
+}
+
+int c2_alloc_qp(struct c2_dev *c2dev,
+               struct c2_pd *pd,
+               struct ib_qp_init_attr *qp_attrs, struct c2_qp *qp)
+{
+       struct c2wr_qp_create_req wr;
+       struct c2wr_qp_create_rep *reply;
+       struct c2_vq_req *vq_req;
+       struct c2_cq *send_cq = to_c2cq(qp_attrs->send_cq);
+       struct c2_cq *recv_cq = to_c2cq(qp_attrs->recv_cq);
+       unsigned long peer_pa;
+       u32 q_size, msg_size, mmap_size;
+       void __iomem *mmap;
+       int err;
+
+       err = c2_alloc_qpn(c2dev, qp);
+       if (err)
+               return err;
+       qp->ibqp.qp_num = qp->qpn;
+       qp->ibqp.qp_type = IB_QPT_RC;
+
+       /* Allocate the SQ and RQ shared pointers */
+       qp->sq_mq.shared = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                        &qp->sq_mq.shared_dma, GFP_KERNEL);
+       if (!qp->sq_mq.shared) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       qp->rq_mq.shared = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                        &qp->rq_mq.shared_dma, GFP_KERNEL);
+       if (!qp->rq_mq.shared) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       /* Allocate the verbs request */
+       vq_req = vq_req_alloc(c2dev);
+       if (vq_req == NULL) {
+               err = -ENOMEM;
+               goto bail2;
+       }
+
+       /* Initialize the work request */
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_QP_CREATE);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+       wr.sq_cq_handle = send_cq->adapter_handle;
+       wr.rq_cq_handle = recv_cq->adapter_handle;
+       wr.sq_depth = cpu_to_be32(qp_attrs->cap.max_send_wr + 1);
+       wr.rq_depth = cpu_to_be32(qp_attrs->cap.max_recv_wr + 1);
+       wr.srq_handle = 0;
+       wr.flags = cpu_to_be32(QP_RDMA_READ | QP_RDMA_WRITE | QP_MW_BIND |
+                              QP_ZERO_STAG | QP_RDMA_READ_RESPONSE);
+       wr.send_sgl_depth = cpu_to_be32(qp_attrs->cap.max_send_sge);
+       wr.recv_sgl_depth = cpu_to_be32(qp_attrs->cap.max_recv_sge);
+       wr.rdma_write_sgl_depth = cpu_to_be32(qp_attrs->cap.max_send_sge);
+       wr.shared_sq_ht = cpu_to_be64(qp->sq_mq.shared_dma);
+       wr.shared_rq_ht = cpu_to_be64(qp->rq_mq.shared_dma);
+       wr.ord = cpu_to_be32(C2_MAX_ORD_PER_QP);
+       wr.ird = cpu_to_be32(C2_MAX_IRD_PER_QP);
+       wr.pd_id = pd->pd_id;
+       wr.user_context = (unsigned long) qp;
+
+       vq_req_get(c2dev, vq_req);
+
+       /* Send the WR to the adapter */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail3;
+       }
+
+       /* Wait for the verb reply  */
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail3;
+       }
+
+       /* Process the reply */
+       reply = (struct c2wr_qp_create_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail3;
+       }
+
+       if ((err = c2_wr_get_result(reply)) != 0) {
+               goto bail4;
+       }
+
+       /* Fill in the kernel QP struct */
+       atomic_set(&qp->refcount, 1);
+       qp->adapter_handle = reply->qp_handle;
+       qp->state = IB_QPS_RESET;
+       qp->send_sgl_depth = qp_attrs->cap.max_send_sge;
+       qp->rdma_write_sgl_depth = qp_attrs->cap.max_send_sge;
+       qp->recv_sgl_depth = qp_attrs->cap.max_recv_sge;
+
+       /* Initialize the SQ MQ */
+       q_size = be32_to_cpu(reply->sq_depth);
+       msg_size = be32_to_cpu(reply->sq_msg_size);
+       peer_pa = c2dev->pa + be32_to_cpu(reply->sq_mq_start);
+       mmap_size = PAGE_ALIGN(sizeof(struct c2_mq_shared) + msg_size * q_size);
+       mmap = ioremap_nocache(peer_pa, mmap_size);
+       if (!mmap) {
+               err = -ENOMEM;
+               goto bail5;
+       }
+
+       c2_mq_req_init(&qp->sq_mq,
+                      be32_to_cpu(reply->sq_mq_index),
+                      q_size,
+                      msg_size,
+                      mmap + sizeof(struct c2_mq_shared),      /* pool start */
+                      mmap,                            /* peer */
+                      C2_MQ_ADAPTER_TARGET);
+
+       /* Initialize the RQ mq */
+       q_size = be32_to_cpu(reply->rq_depth);
+       msg_size = be32_to_cpu(reply->rq_msg_size);
+       peer_pa = c2dev->pa + be32_to_cpu(reply->rq_mq_start);
+       mmap_size = PAGE_ALIGN(sizeof(struct c2_mq_shared) + msg_size * q_size);
+       mmap = ioremap_nocache(peer_pa, mmap_size);
+       if (!mmap) {
+               err = -ENOMEM;
+               goto bail6;
+       }
+
+       c2_mq_req_init(&qp->rq_mq,
+                      be32_to_cpu(reply->rq_mq_index),
+                      q_size,
+                      msg_size,
+                      mmap + sizeof(struct c2_mq_shared),      /* pool start */
+                      mmap,                            /* peer */
+                      C2_MQ_ADAPTER_TARGET);
+
+       vq_repbuf_free(c2dev, reply);
+       vq_req_free(c2dev, vq_req);
+
+       return 0;
+
+      bail6:
+       iounmap(qp->sq_mq.peer);
+      bail5:
+       destroy_qp(c2dev, qp);
+      bail4:
+       vq_repbuf_free(c2dev, reply);
+      bail3:
+       vq_req_free(c2dev, vq_req);
+      bail2:
+       c2_free_mqsp(qp->rq_mq.shared);
+      bail1:
+       c2_free_mqsp(qp->sq_mq.shared);
+      bail0:
+       c2_free_qpn(c2dev, qp->qpn);
+       return err;
+}
+
+void c2_free_qp(struct c2_dev *c2dev, struct c2_qp *qp)
+{
+       struct c2_cq *send_cq;
+       struct c2_cq *recv_cq;
+
+       send_cq = to_c2cq(qp->ibqp.send_cq);
+       recv_cq = to_c2cq(qp->ibqp.recv_cq);
+
+       /*
+        * Lock CQs here, so that CQ polling code can do QP lookup
+        * without taking a lock.
+        */
+       spin_lock_irq(&send_cq->lock);
+       if (send_cq != recv_cq)
+               spin_lock(&recv_cq->lock);
+
+       c2_free_qpn(c2dev, qp->qpn);
+
+       if (send_cq != recv_cq)
+               spin_unlock(&recv_cq->lock);
+       spin_unlock_irq(&send_cq->lock);
+
+       /*
+        * Destory qp in the rnic...
+        */
+       destroy_qp(c2dev, qp);
+
+       /*
+        * Mark any unreaped CQEs as null and void.
+        */
+       c2_cq_clean(c2dev, qp, send_cq->cqn);
+       if (send_cq != recv_cq)
+               c2_cq_clean(c2dev, qp, recv_cq->cqn);
+       /*
+        * Unmap the MQs and return the shared pointers
+        * to the message pool.
+        */
+       iounmap(qp->sq_mq.peer);
+       iounmap(qp->rq_mq.peer);
+       c2_free_mqsp(qp->sq_mq.shared);
+       c2_free_mqsp(qp->rq_mq.shared);
+
+       atomic_dec(&qp->refcount);
+       wait_event(qp->wait, !atomic_read(&qp->refcount));
+}
+
+/*
+ * Function: move_sgl
+ *
+ * Description:
+ * Move an SGL from the user's work request struct into a CCIL Work Request
+ * message, swapping to WR byte order and ensure the total length doesn't
+ * overflow.
+ *
+ * IN:
+ * dst         - ptr to CCIL Work Request message SGL memory.
+ * src         - ptr to the consumers SGL memory.
+ *
+ * OUT: none
+ *
+ * Return:
+ * CCIL status codes.
+ */
+static int
+move_sgl(struct c2_data_addr * dst, struct ib_sge *src, int count, u32 * p_len,
+        u8 * actual_count)
+{
+       u32 tot = 0;            /* running total */
+       u8 acount = 0;          /* running total non-0 len sge's */
+
+       while (count > 0) {
+               /*
+                * If the addition of this SGE causes the
+                * total SGL length to exceed 2^32-1, then
+                * fail-n-bail.
+                *
+                * If the current total plus the next element length
+                * wraps, then it will go negative and be less than the
+                * current total...
+                */
+               if ((tot + src->length) < tot) {
+                       return -EINVAL;
+               }
+               /*
+                * Bug: 1456 (as well as 1498 & 1643)
+                * Skip over any sge's supplied with len=0
+                */
+               if (src->length) {
+                       tot += src->length;
+                       dst->stag = cpu_to_be32(src->lkey);
+                       dst->to = cpu_to_be64(src->addr);
+                       dst->length = cpu_to_be32(src->length);
+                       dst++;
+                       acount++;
+               }
+               src++;
+               count--;
+       }
+
+       if (acount == 0) {
+               /*
+                * Bug: 1476 (as well as 1498, 1456 and 1643)
+                * Setup the SGL in the WR to make it easier for the RNIC.
+                * This way, the FW doesn't have to deal with special cases.
+                * Setting length=0 should be sufficient.
+                */
+               dst->stag = 0;
+               dst->to = 0;
+               dst->length = 0;
+       }
+
+       *p_len = tot;
+       *actual_count = acount;
+       return 0;
+}
+
+/*
+ * Function: c2_activity (private function)
+ *
+ * Description:
+ * Post an mq index to the host->adapter activity fifo.
+ *
+ * IN:
+ * c2dev       - ptr to c2dev structure
+ * mq_index    - mq index to post
+ * shared      - value most recently written to shared
+ *
+ * OUT:
+ *
+ * Return:
+ * none
+ */
+static inline void c2_activity(struct c2_dev *c2dev, u32 mq_index, u16 shared)
+{
+       /*
+        * First read the register to see if the FIFO is full, and if so,
+        * spin until it's not.  This isn't perfect -- there is no
+        * synchronization among the clients of the register, but in
+        * practice it prevents multiple CPU from hammering the bus
+        * with PCI RETRY. Note that when this does happen, the card
+        * cannot get on the bus and the card and system hang in a
+        * deadlock -- thus the need for this code. [TOT]
+        */
+       while (readl(c2dev->regs + PCI_BAR0_ADAPTER_HINT) & 0x80000000) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(0);
+       }
+
+       __raw_writel(C2_HINT_MAKE(mq_index, shared),
+                    c2dev->regs + PCI_BAR0_ADAPTER_HINT);
+}
+
+/*
+ * Function: qp_wr_post
+ *
+ * Description:
+ * This in-line function allocates a MQ msg, then moves the host-copy of
+ * the completed WR into msg.  Then it posts the message.
+ *
+ * IN:
+ * q           - ptr to user MQ.
+ * wr          - ptr to host-copy of the WR.
+ * qp          - ptr to user qp
+ * size                - Number of bytes to post.  Assumed to be divisible by 4.
+ *
+ * OUT: none
+ *
+ * Return:
+ * CCIL status codes.
+ */
+static int qp_wr_post(struct c2_mq *q, union c2wr * wr, struct c2_qp *qp, u32 size)
+{
+       union c2wr *msg;
+
+       msg = c2_mq_alloc(q);
+       if (msg == NULL) {
+               return -EINVAL;
+       }
+#ifdef CCMSGMAGIC
+       ((c2wr_hdr_t *) wr)->magic = cpu_to_be32(CCWR_MAGIC);
+#endif
+
+       /*
+        * Since all header fields in the WR are the same as the
+        * CQE, set the following so the adapter need not.
+        */
+       c2_wr_set_result(wr, CCERR_PENDING);
+
+       /*
+        * Copy the wr down to the adapter
+        */
+       memcpy((void *) msg, (void *) wr, size);
+
+       c2_mq_produce(q);
+       return 0;
+}
+
+
+int c2_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
+                struct ib_send_wr **bad_wr)
+{
+       struct c2_dev *c2dev = to_c2dev(ibqp->device);
+       struct c2_qp *qp = to_c2qp(ibqp);
+       union c2wr wr;
+       int err = 0;
+
+       u32 flags;
+       u32 tot_len;
+       u8 actual_sge_count;
+       u32 msg_size;
+
+       if (qp->state > IB_QPS_RTS)
+               return -EINVAL;
+
+       while (ib_wr) {
+
+               flags = 0;
+               wr.sqwr.sq_hdr.user_hdr.hdr.context = ib_wr->wr_id;
+               if (ib_wr->send_flags & IB_SEND_SIGNALED) {
+                       flags |= SQ_SIGNALED;
+               }
+
+               switch (ib_wr->opcode) {
+               case IB_WR_SEND:
+                       if (ib_wr->send_flags & IB_SEND_SOLICITED) {
+                               c2_wr_set_id(&wr, C2_WR_TYPE_SEND_SE);
+                               msg_size = sizeof(struct c2wr_send_req);
+                       } else {
+                               c2_wr_set_id(&wr, C2_WR_TYPE_SEND);
+                               msg_size = sizeof(struct c2wr_send_req);
+                       }
+
+                       wr.sqwr.send.remote_stag = 0;
+                       msg_size += sizeof(struct c2_data_addr) * ib_wr->num_sge;
+                       if (ib_wr->num_sge > qp->send_sgl_depth) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       if (ib_wr->send_flags & IB_SEND_FENCE) {
+                               flags |= SQ_READ_FENCE;
+                       }
+                       err = move_sgl((struct c2_data_addr *) & (wr.sqwr.send.data),
+                                      ib_wr->sg_list,
+                                      ib_wr->num_sge,
+                                      &tot_len, &actual_sge_count);
+                       wr.sqwr.send.sge_len = cpu_to_be32(tot_len);
+                       c2_wr_set_sge_count(&wr, actual_sge_count);
+                       break;
+               case IB_WR_RDMA_WRITE:
+                       c2_wr_set_id(&wr, C2_WR_TYPE_RDMA_WRITE);
+                       msg_size = sizeof(struct c2wr_rdma_write_req) +
+                           (sizeof(struct c2_data_addr) * ib_wr->num_sge);
+                       if (ib_wr->num_sge > qp->rdma_write_sgl_depth) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       if (ib_wr->send_flags & IB_SEND_FENCE) {
+                               flags |= SQ_READ_FENCE;
+                       }
+                       wr.sqwr.rdma_write.remote_stag =
+                           cpu_to_be32(ib_wr->wr.rdma.rkey);
+                       wr.sqwr.rdma_write.remote_to =
+                           cpu_to_be64(ib_wr->wr.rdma.remote_addr);
+                       err = move_sgl((struct c2_data_addr *)
+                                      & (wr.sqwr.rdma_write.data),
+                                      ib_wr->sg_list,
+                                      ib_wr->num_sge,
+                                      &tot_len, &actual_sge_count);
+                       wr.sqwr.rdma_write.sge_len = cpu_to_be32(tot_len);
+                       c2_wr_set_sge_count(&wr, actual_sge_count);
+                       break;
+               case IB_WR_RDMA_READ:
+                       c2_wr_set_id(&wr, C2_WR_TYPE_RDMA_READ);
+                       msg_size = sizeof(struct c2wr_rdma_read_req);
+
+                       /* IWarp only suppots 1 sge for RDMA reads */
+                       if (ib_wr->num_sge > 1) {
+                               err = -EINVAL;
+                               break;
+                       }
+
+                       /*
+                        * Move the local and remote stag/to/len into the WR.
+                        */
+                       wr.sqwr.rdma_read.local_stag =
+                           cpu_to_be32(ib_wr->sg_list->lkey);
+                       wr.sqwr.rdma_read.local_to =
+                           cpu_to_be64(ib_wr->sg_list->addr);
+                       wr.sqwr.rdma_read.remote_stag =
+                           cpu_to_be32(ib_wr->wr.rdma.rkey);
+                       wr.sqwr.rdma_read.remote_to =
+                           cpu_to_be64(ib_wr->wr.rdma.remote_addr);
+                       wr.sqwr.rdma_read.length =
+                           cpu_to_be32(ib_wr->sg_list->length);
+                       break;
+               default:
+                       /* error */
+                       msg_size = 0;
+                       err = -EINVAL;
+                       break;
+               }
+
+               /*
+                * If we had an error on the last wr build, then
+                * break out.  Possible errors include bogus WR
+                * type, and a bogus SGL length...
+                */
+               if (err) {
+                       break;
+               }
+
+               /*
+                * Store flags
+                */
+               c2_wr_set_flags(&wr, flags);
+
+               /*
+                * Post the puppy!
+                */
+               err = qp_wr_post(&qp->sq_mq, &wr, qp, msg_size);
+               if (err) {
+                       break;
+               }
+
+               /*
+                * Enqueue mq index to activity FIFO.
+                */
+               c2_activity(c2dev, qp->sq_mq.index, qp->sq_mq.hint_count);
+
+               ib_wr = ib_wr->next;
+       }
+
+       if (err)
+               *bad_wr = ib_wr;
+       return err;
+}
+
+int c2_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr,
+                   struct ib_recv_wr **bad_wr)
+{
+       struct c2_dev *c2dev = to_c2dev(ibqp->device);
+       struct c2_qp *qp = to_c2qp(ibqp);
+       union c2wr wr;
+       int err = 0;
+
+       if (qp->state > IB_QPS_RTS)
+               return -EINVAL;
+
+       /*
+        * Try and post each work request
+        */
+       while (ib_wr) {
+               u32 tot_len;
+               u8 actual_sge_count;
+
+               if (ib_wr->num_sge > qp->recv_sgl_depth) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               /*
+                * Create local host-copy of the WR
+                */
+               wr.rqwr.rq_hdr.user_hdr.hdr.context = ib_wr->wr_id;
+               c2_wr_set_id(&wr, CCWR_RECV);
+               c2_wr_set_flags(&wr, 0);
+
+               /* sge_count is limited to eight bits. */
+               BUG_ON(ib_wr->num_sge >= 256);
+               err = move_sgl((struct c2_data_addr *) & (wr.rqwr.data),
+                              ib_wr->sg_list,
+                              ib_wr->num_sge, &tot_len, &actual_sge_count);
+               c2_wr_set_sge_count(&wr, actual_sge_count);
+
+               /*
+                * If we had an error on the last wr build, then
+                * break out.  Possible errors include bogus WR
+                * type, and a bogus SGL length...
+                */
+               if (err) {
+                       break;
+               }
+
+               err = qp_wr_post(&qp->rq_mq, &wr, qp, qp->rq_mq.msg_size);
+               if (err) {
+                       break;
+               }
+
+               /*
+                * Enqueue mq index to activity FIFO
+                */
+               c2_activity(c2dev, qp->rq_mq.index, qp->rq_mq.hint_count);
+
+               ib_wr = ib_wr->next;
+       }
+
+       if (err)
+               *bad_wr = ib_wr;
+       return err;
+}
+
+void __devinit c2_init_qp_table(struct c2_dev *c2dev)
+{
+       spin_lock_init(&c2dev->qp_table.lock);
+       idr_init(&c2dev->qp_table.idr);
+}
+
+void __devexit c2_cleanup_qp_table(struct c2_dev *c2dev)
+{
+       idr_destroy(&c2dev->qp_table.idr);
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c
new file mode 100644 (file)
index 0000000..1c3c9d6
--- /dev/null
@@ -0,0 +1,663 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.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 <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/inet.h>
+
+#include <linux/route.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <rdma/ib_smi.h>
+#include "c2.h"
+#include "c2_vq.h"
+
+/* Device capabilities */
+#define C2_MIN_PAGESIZE  1024
+
+#define C2_MAX_MRS       32768
+#define C2_MAX_QPS       16000
+#define C2_MAX_WQE_SZ    256
+#define C2_MAX_QP_WR     ((128*1024)/C2_MAX_WQE_SZ)
+#define C2_MAX_SGES      4
+#define C2_MAX_SGE_RD    1
+#define C2_MAX_CQS       32768
+#define C2_MAX_CQES      4096
+#define C2_MAX_PDS       16384
+
+/*
+ * Send the adapter INIT message to the amso1100
+ */
+static int c2_adapter_init(struct c2_dev *c2dev)
+{
+       struct c2wr_init_req wr;
+       int err;
+
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_INIT);
+       wr.hdr.context = 0;
+       wr.hint_count = cpu_to_be64(c2dev->hint_count_dma);
+       wr.q0_host_shared = cpu_to_be64(c2dev->req_vq.shared_dma);
+       wr.q1_host_shared = cpu_to_be64(c2dev->rep_vq.shared_dma);
+       wr.q1_host_msg_pool = cpu_to_be64(c2dev->rep_vq.host_dma);
+       wr.q2_host_shared = cpu_to_be64(c2dev->aeq.shared_dma);
+       wr.q2_host_msg_pool = cpu_to_be64(c2dev->aeq.host_dma);
+
+       /* Post the init message */
+       err = vq_send_wr(c2dev, (union c2wr *) & wr);
+
+       return err;
+}
+
+/*
+ * Send the adapter TERM message to the amso1100
+ */
+static void c2_adapter_term(struct c2_dev *c2dev)
+{
+       struct c2wr_init_req wr;
+
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_TERM);
+       wr.hdr.context = 0;
+
+       /* Post the init message */
+       vq_send_wr(c2dev, (union c2wr *) & wr);
+       c2dev->init = 0;
+
+       return;
+}
+
+/*
+ * Query the adapter
+ */
+static int c2_rnic_query(struct c2_dev *c2dev, struct ib_device_attr *props)
+{
+       struct c2_vq_req *vq_req;
+       struct c2wr_rnic_query_req wr;
+       struct c2wr_rnic_query_rep *reply;
+       int err;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       c2_wr_set_id(&wr, CCWR_RNIC_QUERY);
+       wr.hdr.context = (unsigned long) vq_req;
+       wr.rnic_handle = c2dev->adapter_handle;
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) &wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail1;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail1;
+
+       reply =
+           (struct c2wr_rnic_query_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply)
+               err = -ENOMEM;
+
+       err = c2_errno(reply);
+       if (err)
+               goto bail2;
+
+       props->fw_ver =
+               ((u64)be32_to_cpu(reply->fw_ver_major) << 32) |
+               ((be32_to_cpu(reply->fw_ver_minor) && 0xFFFF) << 16) |
+               (be32_to_cpu(reply->fw_ver_patch) && 0xFFFF);
+       memcpy(&props->sys_image_guid, c2dev->netdev->dev_addr, 6);
+       props->max_mr_size         = 0xFFFFFFFF;
+       props->page_size_cap       = ~(C2_MIN_PAGESIZE-1);
+       props->vendor_id           = be32_to_cpu(reply->vendor_id);
+       props->vendor_part_id      = be32_to_cpu(reply->part_number);
+       props->hw_ver              = be32_to_cpu(reply->hw_version);
+       props->max_qp              = be32_to_cpu(reply->max_qps);
+       props->max_qp_wr           = be32_to_cpu(reply->max_qp_depth);
+       props->device_cap_flags    = c2dev->device_cap_flags;
+       props->max_sge             = C2_MAX_SGES;
+       props->max_sge_rd          = C2_MAX_SGE_RD;
+       props->max_cq              = be32_to_cpu(reply->max_cqs);
+       props->max_cqe             = be32_to_cpu(reply->max_cq_depth);
+       props->max_mr              = be32_to_cpu(reply->max_mrs);
+       props->max_pd              = be32_to_cpu(reply->max_pds);
+       props->max_qp_rd_atom      = be32_to_cpu(reply->max_qp_ird);
+       props->max_ee_rd_atom      = 0;
+       props->max_res_rd_atom     = be32_to_cpu(reply->max_global_ird);
+       props->max_qp_init_rd_atom = be32_to_cpu(reply->max_qp_ord);
+       props->max_ee_init_rd_atom = 0;
+       props->atomic_cap          = IB_ATOMIC_NONE;
+       props->max_ee              = 0;
+       props->max_rdd             = 0;
+       props->max_mw              = be32_to_cpu(reply->max_mws);
+       props->max_raw_ipv6_qp     = 0;
+       props->max_raw_ethy_qp     = 0;
+       props->max_mcast_grp       = 0;
+       props->max_mcast_qp_attach = 0;
+       props->max_total_mcast_qp_attach = 0;
+       props->max_ah              = 0;
+       props->max_fmr             = 0;
+       props->max_map_per_fmr     = 0;
+       props->max_srq             = 0;
+       props->max_srq_wr          = 0;
+       props->max_srq_sge         = 0;
+       props->max_pkeys           = 0;
+       props->local_ca_ack_delay  = 0;
+
+ bail2:
+       vq_repbuf_free(c2dev, reply);
+
+ bail1:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+/*
+ * Add an IP address to the RNIC interface
+ */
+int c2_add_addr(struct c2_dev *c2dev, u32 inaddr, u32 inmask)
+{
+       struct c2_vq_req *vq_req;
+       struct c2wr_rnic_setconfig_req *wr;
+       struct c2wr_rnic_setconfig_rep *reply;
+       struct c2_netaddr netaddr;
+       int err, len;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       len = sizeof(struct c2_netaddr);
+       wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
+       if (!wr) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       c2_wr_set_id(wr, CCWR_RNIC_SETCONFIG);
+       wr->hdr.context = (unsigned long) vq_req;
+       wr->rnic_handle = c2dev->adapter_handle;
+       wr->option = cpu_to_be32(C2_CFG_ADD_ADDR);
+
+       netaddr.ip_addr = inaddr;
+       netaddr.netmask = inmask;
+       netaddr.mtu = 0;
+
+       memcpy(wr->data, &netaddr, len);
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail1;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail1;
+
+       reply =
+           (struct c2wr_rnic_setconfig_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       err = c2_errno(reply);
+       vq_repbuf_free(c2dev, reply);
+
+      bail1:
+       kfree(wr);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+/*
+ * Delete an IP address from the RNIC interface
+ */
+int c2_del_addr(struct c2_dev *c2dev, u32 inaddr, u32 inmask)
+{
+       struct c2_vq_req *vq_req;
+       struct c2wr_rnic_setconfig_req *wr;
+       struct c2wr_rnic_setconfig_rep *reply;
+       struct c2_netaddr netaddr;
+       int err, len;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (!vq_req)
+               return -ENOMEM;
+
+       len = sizeof(struct c2_netaddr);
+       wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
+       if (!wr) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       c2_wr_set_id(wr, CCWR_RNIC_SETCONFIG);
+       wr->hdr.context = (unsigned long) vq_req;
+       wr->rnic_handle = c2dev->adapter_handle;
+       wr->option = cpu_to_be32(C2_CFG_DEL_ADDR);
+
+       netaddr.ip_addr = inaddr;
+       netaddr.netmask = inmask;
+       netaddr.mtu = 0;
+
+       memcpy(wr->data, &netaddr, len);
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, (union c2wr *) wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail1;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err)
+               goto bail1;
+
+       reply =
+           (struct c2wr_rnic_setconfig_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       err = c2_errno(reply);
+       vq_repbuf_free(c2dev, reply);
+
+      bail1:
+       kfree(wr);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+/*
+ * Open a single RNIC instance to use with all
+ * low level openib calls
+ */
+static int c2_rnic_open(struct c2_dev *c2dev)
+{
+       struct c2_vq_req *vq_req;
+       union c2wr wr;
+       struct c2wr_rnic_open_rep *reply;
+       int err;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (vq_req == NULL) {
+               return -ENOMEM;
+       }
+
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_RNIC_OPEN);
+       wr.rnic_open.req.hdr.context = (unsigned long) (vq_req);
+       wr.rnic_open.req.flags = cpu_to_be16(RNIC_PRIV_MODE);
+       wr.rnic_open.req.port_num = cpu_to_be16(0);
+       wr.rnic_open.req.user_context = (unsigned long) c2dev;
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, &wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail0;
+       }
+
+       reply = (struct c2wr_rnic_open_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       if ((err = c2_errno(reply)) != 0) {
+               goto bail1;
+       }
+
+       c2dev->adapter_handle = reply->rnic_handle;
+
+      bail1:
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+/*
+ * Close the RNIC instance
+ */
+static int c2_rnic_close(struct c2_dev *c2dev)
+{
+       struct c2_vq_req *vq_req;
+       union c2wr wr;
+       struct c2wr_rnic_close_rep *reply;
+       int err;
+
+       vq_req = vq_req_alloc(c2dev);
+       if (vq_req == NULL) {
+               return -ENOMEM;
+       }
+
+       memset(&wr, 0, sizeof(wr));
+       c2_wr_set_id(&wr, CCWR_RNIC_CLOSE);
+       wr.rnic_close.req.hdr.context = (unsigned long) vq_req;
+       wr.rnic_close.req.rnic_handle = c2dev->adapter_handle;
+
+       vq_req_get(c2dev, vq_req);
+
+       err = vq_send_wr(c2dev, &wr);
+       if (err) {
+               vq_req_put(c2dev, vq_req);
+               goto bail0;
+       }
+
+       err = vq_wait_for_reply(c2dev, vq_req);
+       if (err) {
+               goto bail0;
+       }
+
+       reply = (struct c2wr_rnic_close_rep *) (unsigned long) (vq_req->reply_msg);
+       if (!reply) {
+               err = -ENOMEM;
+               goto bail0;
+       }
+
+       if ((err = c2_errno(reply)) != 0) {
+               goto bail1;
+       }
+
+       c2dev->adapter_handle = 0;
+
+      bail1:
+       vq_repbuf_free(c2dev, reply);
+      bail0:
+       vq_req_free(c2dev, vq_req);
+       return err;
+}
+
+/*
+ * Called by c2_probe to initialize the RNIC. This principally
+ * involves initalizing the various limits and resouce pools that
+ * comprise the RNIC instance.
+ */
+int c2_rnic_init(struct c2_dev *c2dev)
+{
+       int err;
+       u32 qsize, msgsize;
+       void *q1_pages;
+       void *q2_pages;
+       void __iomem *mmio_regs;
+
+       /* Device capabilities */
+       c2dev->device_cap_flags =
+           (IB_DEVICE_RESIZE_MAX_WR |
+            IB_DEVICE_CURR_QP_STATE_MOD |
+            IB_DEVICE_SYS_IMAGE_GUID |
+            IB_DEVICE_ZERO_STAG |
+            IB_DEVICE_SEND_W_INV | IB_DEVICE_MEM_WINDOW);
+
+       /* Allocate the qptr_array */
+       c2dev->qptr_array = vmalloc(C2_MAX_CQS * sizeof(void *));
+       if (!c2dev->qptr_array) {
+               return -ENOMEM;
+       }
+
+       /* Inialize the qptr_array */
+       memset(c2dev->qptr_array, 0, C2_MAX_CQS * sizeof(void *));
+       c2dev->qptr_array[0] = (void *) &c2dev->req_vq;
+       c2dev->qptr_array[1] = (void *) &c2dev->rep_vq;
+       c2dev->qptr_array[2] = (void *) &c2dev->aeq;
+
+       /* Initialize data structures */
+       init_waitqueue_head(&c2dev->req_vq_wo);
+       spin_lock_init(&c2dev->vqlock);
+       spin_lock_init(&c2dev->lock);
+
+       /* Allocate MQ shared pointer pool for kernel clients. User
+        * mode client pools are hung off the user context
+        */
+       err = c2_init_mqsp_pool(c2dev, GFP_KERNEL, &c2dev->kern_mqsp_pool);
+       if (err) {
+               goto bail0;
+       }
+
+       /* Allocate shared pointers for Q0, Q1, and Q2 from
+        * the shared pointer pool.
+        */
+
+       c2dev->hint_count = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                            &c2dev->hint_count_dma,
+                                            GFP_KERNEL);
+       c2dev->req_vq.shared = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                            &c2dev->req_vq.shared_dma,
+                                            GFP_KERNEL);
+       c2dev->rep_vq.shared = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                            &c2dev->rep_vq.shared_dma,
+                                            GFP_KERNEL);
+       c2dev->aeq.shared = c2_alloc_mqsp(c2dev, c2dev->kern_mqsp_pool,
+                                         &c2dev->aeq.shared_dma, GFP_KERNEL);
+       if (!c2dev->hint_count || !c2dev->req_vq.shared ||
+           !c2dev->rep_vq.shared || !c2dev->aeq.shared) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+
+       mmio_regs = c2dev->kva;
+       /* Initialize the Verbs Request Queue */
+       c2_mq_req_init(&c2dev->req_vq, 0,
+                      be32_to_cpu(readl(mmio_regs + C2_REGS_Q0_QSIZE)),
+                      be32_to_cpu(readl(mmio_regs + C2_REGS_Q0_MSGSIZE)),
+                      mmio_regs +
+                      be32_to_cpu(readl(mmio_regs + C2_REGS_Q0_POOLSTART)),
+                      mmio_regs +
+                      be32_to_cpu(readl(mmio_regs + C2_REGS_Q0_SHARED)),
+                      C2_MQ_ADAPTER_TARGET);
+
+       /* Initialize the Verbs Reply Queue */
+       qsize = be32_to_cpu(readl(mmio_regs + C2_REGS_Q1_QSIZE));
+       msgsize = be32_to_cpu(readl(mmio_regs + C2_REGS_Q1_MSGSIZE));
+       q1_pages = kmalloc(qsize * msgsize, GFP_KERNEL);
+       if (!q1_pages) {
+               err = -ENOMEM;
+               goto bail1;
+       }
+       c2dev->rep_vq.host_dma = dma_map_single(c2dev->ibdev.dma_device,
+                                               (void *)q1_pages, qsize * msgsize,
+                                               DMA_FROM_DEVICE);
+       pci_unmap_addr_set(&c2dev->rep_vq, mapping, c2dev->rep_vq.host_dma);
+       pr_debug("%s rep_vq va %p dma %llx\n", __FUNCTION__, q1_pages,
+                (u64)c2dev->rep_vq.host_dma);
+       c2_mq_rep_init(&c2dev->rep_vq,
+                  1,
+                  qsize,
+                  msgsize,
+                  q1_pages,
+                  mmio_regs +
+                  be32_to_cpu(readl(mmio_regs + C2_REGS_Q1_SHARED)),
+                  C2_MQ_HOST_TARGET);
+
+       /* Initialize the Asynchronus Event Queue */
+       qsize = be32_to_cpu(readl(mmio_regs + C2_REGS_Q2_QSIZE));
+       msgsize = be32_to_cpu(readl(mmio_regs + C2_REGS_Q2_MSGSIZE));
+       q2_pages = kmalloc(qsize * msgsize, GFP_KERNEL);
+       if (!q2_pages) {
+               err = -ENOMEM;
+               goto bail2;
+       }
+       c2dev->aeq.host_dma = dma_map_single(c2dev->ibdev.dma_device,
+                                               (void *)q2_pages, qsize * msgsize,
+                                               DMA_FROM_DEVICE);
+       pci_unmap_addr_set(&c2dev->aeq, mapping, c2dev->aeq.host_dma);
+       pr_debug("%s aeq va %p dma %llx\n", __FUNCTION__, q1_pages,
+                (u64)c2dev->rep_vq.host_dma);
+       c2_mq_rep_init(&c2dev->aeq,
+                      2,
+                      qsize,
+                      msgsize,
+                      q2_pages,
+                      mmio_regs +
+                      be32_to_cpu(readl(mmio_regs + C2_REGS_Q2_SHARED)),
+                      C2_MQ_HOST_TARGET);
+
+       /* Initialize the verbs request allocator */
+       err = vq_init(c2dev);
+       if (err)
+               goto bail3;
+
+       /* Enable interrupts on the adapter */
+       writel(0, c2dev->regs + C2_IDIS);
+
+       /* create the WR init message */
+       err = c2_adapter_init(c2dev);
+       if (err)
+               goto bail4;
+       c2dev->init++;
+
+       /* open an adapter instance */
+       err = c2_rnic_open(c2dev);
+       if (err)
+               goto bail4;
+
+       /* Initialize cached the adapter limits */
+       if (c2_rnic_query(c2dev, &c2dev->props))
+               goto bail5;
+
+       /* Initialize the PD pool */
+       err = c2_init_pd_table(c2dev);
+       if (err)
+               goto bail5;
+
+       /* Initialize the QP pool */
+       c2_init_qp_table(c2dev);
+       return 0;
+
+      bail5:
+       c2_rnic_close(c2dev);
+      bail4:
+       vq_term(c2dev);
+      bail3:
+       dma_unmap_single(c2dev->ibdev.dma_device,
+                        pci_unmap_addr(&c2dev->aeq, mapping),
+                        c2dev->aeq.q_size * c2dev->aeq.msg_size,
+                        DMA_FROM_DEVICE);
+       kfree(q2_pages);
+      bail2:
+       dma_unmap_single(c2dev->ibdev.dma_device,
+                        pci_unmap_addr(&c2dev->rep_vq, mapping),
+                        c2dev->rep_vq.q_size * c2dev->rep_vq.msg_size,
+                        DMA_FROM_DEVICE);
+       kfree(q1_pages);
+      bail1:
+       c2_free_mqsp_pool(c2dev, c2dev->kern_mqsp_pool);
+      bail0:
+       vfree(c2dev->qptr_array);
+
+       return err;
+}
+
+/*
+ * Called by c2_remove to cleanup the RNIC resources.
+ */
+void c2_rnic_term(struct c2_dev *c2dev)
+{
+
+       /* Close the open adapter instance */
+       c2_rnic_close(c2dev);
+
+       /* Send the TERM message to the adapter */
+       c2_adapter_term(c2dev);
+
+       /* Disable interrupts on the adapter */
+       writel(1, c2dev->regs + C2_IDIS);
+
+       /* Free the QP pool */
+       c2_cleanup_qp_table(c2dev);
+
+       /* Free the PD pool */
+       c2_cleanup_pd_table(c2dev);
+
+       /* Free the verbs request allocator */
+       vq_term(c2dev);
+
+       /* Unmap and free the asynchronus event queue */
+       dma_unmap_single(c2dev->ibdev.dma_device,
+                        pci_unmap_addr(&c2dev->aeq, mapping),
+                        c2dev->aeq.q_size * c2dev->aeq.msg_size,
+                        DMA_FROM_DEVICE);
+       kfree(c2dev->aeq.msg_pool.host);
+
+       /* Unmap and free the verbs reply queue */
+       dma_unmap_single(c2dev->ibdev.dma_device,
+                        pci_unmap_addr(&c2dev->rep_vq, mapping),
+                        c2dev->rep_vq.q_size * c2dev->rep_vq.msg_size,
+                        DMA_FROM_DEVICE);
+       kfree(c2dev->rep_vq.msg_pool.host);
+
+       /* Free the MQ shared pointer pool */
+       c2_free_mqsp_pool(c2dev, c2dev->kern_mqsp_pool);
+
+       /* Free the qptr_array */
+       vfree(c2dev->qptr_array);
+
+       return;
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_status.h b/drivers/infiniband/hw/amso1100/c2_status.h
new file mode 100644 (file)
index 0000000..6ee4aa9
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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        _C2_STATUS_H_
+#define _C2_STATUS_H_
+
+/*
+ * Verbs Status Codes
+ */
+enum c2_status {
+       C2_OK = 0,              /* This must be zero */
+       CCERR_INSUFFICIENT_RESOURCES = 1,
+       CCERR_INVALID_MODIFIER = 2,
+       CCERR_INVALID_MODE = 3,
+       CCERR_IN_USE = 4,
+       CCERR_INVALID_RNIC = 5,
+       CCERR_INTERRUPTED_OPERATION = 6,
+       CCERR_INVALID_EH = 7,
+       CCERR_INVALID_CQ = 8,
+       CCERR_CQ_EMPTY = 9,
+       CCERR_NOT_IMPLEMENTED = 10,
+       CCERR_CQ_DEPTH_TOO_SMALL = 11,
+       CCERR_PD_IN_USE = 12,
+       CCERR_INVALID_PD = 13,
+       CCERR_INVALID_SRQ = 14,
+       CCERR_INVALID_ADDRESS = 15,
+       CCERR_INVALID_NETMASK = 16,
+       CCERR_INVALID_QP = 17,
+       CCERR_INVALID_QP_STATE = 18,
+       CCERR_TOO_MANY_WRS_POSTED = 19,
+       CCERR_INVALID_WR_TYPE = 20,
+       CCERR_INVALID_SGL_LENGTH = 21,
+       CCERR_INVALID_SQ_DEPTH = 22,
+       CCERR_INVALID_RQ_DEPTH = 23,
+       CCERR_INVALID_ORD = 24,
+       CCERR_INVALID_IRD = 25,
+       CCERR_QP_ATTR_CANNOT_CHANGE = 26,
+       CCERR_INVALID_STAG = 27,
+       CCERR_QP_IN_USE = 28,
+       CCERR_OUTSTANDING_WRS = 29,
+       CCERR_STAG_IN_USE = 30,
+       CCERR_INVALID_STAG_INDEX = 31,
+       CCERR_INVALID_SGL_FORMAT = 32,
+       CCERR_ADAPTER_TIMEOUT = 33,
+       CCERR_INVALID_CQ_DEPTH = 34,
+       CCERR_INVALID_PRIVATE_DATA_LENGTH = 35,
+       CCERR_INVALID_EP = 36,
+       CCERR_MR_IN_USE = CCERR_STAG_IN_USE,
+       CCERR_FLUSHED = 38,
+       CCERR_INVALID_WQE = 39,
+       CCERR_LOCAL_QP_CATASTROPHIC_ERROR = 40,
+       CCERR_REMOTE_TERMINATION_ERROR = 41,
+       CCERR_BASE_AND_BOUNDS_VIOLATION = 42,
+       CCERR_ACCESS_VIOLATION = 43,
+       CCERR_INVALID_PD_ID = 44,
+       CCERR_WRAP_ERROR = 45,
+       CCERR_INV_STAG_ACCESS_ERROR = 46,
+       CCERR_ZERO_RDMA_READ_RESOURCES = 47,
+       CCERR_QP_NOT_PRIVILEGED = 48,
+       CCERR_STAG_STATE_NOT_INVALID = 49,
+       CCERR_INVALID_PAGE_SIZE = 50,
+       CCERR_INVALID_BUFFER_SIZE = 51,
+       CCERR_INVALID_PBE = 52,
+       CCERR_INVALID_FBO = 53,
+       CCERR_INVALID_LENGTH = 54,
+       CCERR_INVALID_ACCESS_RIGHTS = 55,
+       CCERR_PBL_TOO_BIG = 56,
+       CCERR_INVALID_VA = 57,
+       CCERR_INVALID_REGION = 58,
+       CCERR_INVALID_WINDOW = 59,
+       CCERR_TOTAL_LENGTH_TOO_BIG = 60,
+       CCERR_INVALID_QP_ID = 61,
+       CCERR_ADDR_IN_USE = 62,
+       CCERR_ADDR_NOT_AVAIL = 63,
+       CCERR_NET_DOWN = 64,
+       CCERR_NET_UNREACHABLE = 65,
+       CCERR_CONN_ABORTED = 66,
+       CCERR_CONN_RESET = 67,
+       CCERR_NO_BUFS = 68,
+       CCERR_CONN_TIMEDOUT = 69,
+       CCERR_CONN_REFUSED = 70,
+       CCERR_HOST_UNREACHABLE = 71,
+       CCERR_INVALID_SEND_SGL_DEPTH = 72,
+       CCERR_INVALID_RECV_SGL_DEPTH = 73,
+       CCERR_INVALID_RDMA_WRITE_SGL_DEPTH = 74,
+       CCERR_INSUFFICIENT_PRIVILEGES = 75,
+       CCERR_STACK_ERROR = 76,
+       CCERR_INVALID_VERSION = 77,
+       CCERR_INVALID_MTU = 78,
+       CCERR_INVALID_IMAGE = 79,
+       CCERR_PENDING = 98,     /* not an error; user internally by adapter */
+       CCERR_DEFER = 99,       /* not an error; used internally by adapter */
+       CCERR_FAILED_WRITE = 100,
+       CCERR_FAILED_ERASE = 101,
+       CCERR_FAILED_VERIFICATION = 102,
+       CCERR_NOT_FOUND = 103,
+
+};
+
+/*
+ * CCAE_ACTIVE_CONNECT_RESULTS status result codes.
+ */
+enum c2_connect_status {
+       C2_CONN_STATUS_SUCCESS = C2_OK,
+       C2_CONN_STATUS_NO_MEM = CCERR_INSUFFICIENT_RESOURCES,
+       C2_CONN_STATUS_TIMEDOUT = CCERR_CONN_TIMEDOUT,
+       C2_CONN_STATUS_REFUSED = CCERR_CONN_REFUSED,
+       C2_CONN_STATUS_NETUNREACH = CCERR_NET_UNREACHABLE,
+       C2_CONN_STATUS_HOSTUNREACH = CCERR_HOST_UNREACHABLE,
+       C2_CONN_STATUS_INVALID_RNIC = CCERR_INVALID_RNIC,
+       C2_CONN_STATUS_INVALID_QP = CCERR_INVALID_QP,
+       C2_CONN_STATUS_INVALID_QP_STATE = CCERR_INVALID_QP_STATE,
+       C2_CONN_STATUS_REJECTED = CCERR_CONN_RESET,
+       C2_CONN_STATUS_ADDR_NOT_AVAIL = CCERR_ADDR_NOT_AVAIL,
+};
+
+/*
+ * Flash programming status codes.
+ */
+enum c2_flash_status {
+       C2_FLASH_STATUS_SUCCESS = 0x0000,
+       C2_FLASH_STATUS_VERIFY_ERR = 0x0002,
+       C2_FLASH_STATUS_IMAGE_ERR = 0x0004,
+       C2_FLASH_STATUS_ECLBS = 0x0400,
+       C2_FLASH_STATUS_PSLBS = 0x0800,
+       C2_FLASH_STATUS_VPENS = 0x1000,
+};
+
+#endif                         /* _C2_STATUS_H_ */
diff --git a/drivers/infiniband/hw/amso1100/c2_user.h b/drivers/infiniband/hw/amso1100/c2_user.h
new file mode 100644 (file)
index 0000000..7e9e7ad
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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 C2_USER_H
+#define C2_USER_H
+
+#include <linux/types.h>
+
+/*
+ * 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 c2_alloc_ucontext_resp {
+       __u32 qp_tab_size;
+       __u32 uarc_size;
+};
+
+struct c2_alloc_pd_resp {
+       __u32 pdn;
+       __u32 reserved;
+};
+
+struct c2_create_cq {
+       __u32 lkey;
+       __u32 pdn;
+       __u64 arm_db_page;
+       __u64 set_db_page;
+       __u32 arm_db_index;
+       __u32 set_db_index;
+};
+
+struct c2_create_cq_resp {
+       __u32 cqn;
+       __u32 reserved;
+};
+
+struct c2_create_qp {
+       __u32 lkey;
+       __u32 reserved;
+       __u64 sq_db_page;
+       __u64 rq_db_page;
+       __u32 sq_db_index;
+       __u32 rq_db_index;
+};
+
+#endif                         /* C2_USER_H */
diff --git a/drivers/infiniband/hw/amso1100/c2_vq.c b/drivers/infiniband/hw/amso1100/c2_vq.c
new file mode 100644 (file)
index 0000000..40caeb5
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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/slab.h>
+#include <linux/spinlock.h>
+
+#include "c2_vq.h"
+#include "c2_provider.h"
+
+/*
+ * Verbs Request Objects:
+ *
+ * VQ Request Objects are allocated by the kernel verbs handlers.
+ * They contain a wait object, a refcnt, an atomic bool indicating that the
+ * adapter has replied, and a copy of the verb reply work request.
+ * A pointer to the VQ Request Object is passed down in the context
+ * field of the work request message, and reflected back by the adapter
+ * in the verbs reply message.  The function handle_vq() in the interrupt
+ * path will use this pointer to:
+ *     1) append a copy of the verbs reply message
+ *     2) mark that the reply is ready
+ *     3) wake up the kernel verbs handler blocked awaiting the reply.
+ *
+ *
+ * The kernel verbs handlers do a "get" to put a 2nd reference on the
+ * VQ Request object.  If the kernel verbs handler exits before the adapter
+ * can respond, this extra reference will keep the VQ Request object around
+ * until the adapter's reply can be processed.  The reason we need this is
+ * because a pointer to this object is stuffed into the context field of
+ * the verbs work request message, and reflected back in the reply message.
+ * It is used in the interrupt handler (handle_vq()) to wake up the appropriate
+ * kernel verb handler that is blocked awaiting the verb reply.
+ * So handle_vq() will do a "put" on the object when it's done accessing it.
+ * NOTE:  If we guarantee that the kernel verb handler will never bail before
+ *        getting the reply, then we don't need these refcnts.
+ *
+ *
+ * VQ Request objects are freed by the kernel verbs handlers only
+ * after the verb has been processed, or when the adapter fails and
+ * does not reply.
+ *
+ *
+ * Verbs Reply Buffers:
+ *
+ * VQ Reply bufs are local host memory copies of a
+ * outstanding Verb Request reply
+ * message.  The are always allocated by the kernel verbs handlers, and _may_ be
+ * freed by either the kernel verbs handler -or- the interrupt handler.  The
+ * kernel verbs handler _must_ free the repbuf, then free the vq request object
+ * in that order.
+ */
+
+int vq_init(struct c2_dev *c2dev)
+{
+       sprintf(c2dev->vq_cache_name, "c2-vq:dev%c",
+               (char) ('0' + c2dev->devnum));
+       c2dev->host_msg_cache =
+           kmem_cache_create(c2dev->vq_cache_name, c2dev->rep_vq.msg_size, 0,
+                             SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (c2dev->host_msg_cache == NULL) {
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void vq_term(struct c2_dev *c2dev)
+{
+       kmem_cache_destroy(c2dev->host_msg_cache);
+}
+
+/* vq_req_alloc - allocate a VQ Request Object and initialize it.
+ * The refcnt is set to 1.
+ */
+struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev)
+{
+       struct c2_vq_req *r;
+
+       r = kmalloc(sizeof(struct c2_vq_req), GFP_KERNEL);
+       if (r) {
+               init_waitqueue_head(&r->wait_object);
+               r->reply_msg = (u64) NULL;
+               r->event = 0;
+               r->cm_id = NULL;
+               r->qp = NULL;
+               atomic_set(&r->refcnt, 1);
+               atomic_set(&r->reply_ready, 0);
+       }
+       return r;
+}
+
+
+/* vq_req_free - free the VQ Request Object.  It is assumed the verbs handler
+ * has already free the VQ Reply Buffer if it existed.
+ */
+void vq_req_free(struct c2_dev *c2dev, struct c2_vq_req *r)
+{
+       r->reply_msg = (u64) NULL;
+       if (atomic_dec_and_test(&r->refcnt)) {
+               kfree(r);
+       }
+}
+
+/* vq_req_get - reference a VQ Request Object.  Done
+ * only in the kernel verbs handlers.
+ */
+void vq_req_get(struct c2_dev *c2dev, struct c2_vq_req *r)
+{
+       atomic_inc(&r->refcnt);
+}
+
+
+/* vq_req_put - dereference and potentially free a VQ Request Object.
+ *
+ * This is only called by handle_vq() on the
+ * interrupt when it is done processing
+ * a verb reply message.  If the associated
+ * kernel verbs handler has already bailed,
+ * then this put will actually free the VQ
+ * Request object _and_ the VQ Reply Buffer
+ * if it exists.
+ */
+void vq_req_put(struct c2_dev *c2dev, struct c2_vq_req *r)
+{
+       if (atomic_dec_and_test(&r->refcnt)) {
+               if (r->reply_msg != (u64) NULL)
+                       vq_repbuf_free(c2dev,
+                                      (void *) (unsigned long) r->reply_msg);
+               kfree(r);
+       }
+}
+
+
+/*
+ * vq_repbuf_alloc - allocate a VQ Reply Buffer.
+ */
+void *vq_repbuf_alloc(struct c2_dev *c2dev)
+{
+       return kmem_cache_alloc(c2dev->host_msg_cache, SLAB_ATOMIC);
+}
+
+/*
+ * vq_send_wr - post a verbs request message to the Verbs Request Queue.
+ * If a message is not available in the MQ, then block until one is available.
+ * NOTE: handle_mq() on the interrupt context will wake up threads blocked here.
+ * When the adapter drains the Verbs Request Queue,
+ * it inserts MQ index 0 in to the
+ * adapter->host activity fifo and interrupts the host.
+ */
+int vq_send_wr(struct c2_dev *c2dev, union c2wr *wr)
+{
+       void *msg;
+       wait_queue_t __wait;
+
+       /*
+        * grab adapter vq lock
+        */
+       spin_lock(&c2dev->vqlock);
+
+       /*
+        * allocate msg
+        */
+       msg = c2_mq_alloc(&c2dev->req_vq);
+
+       /*
+        * If we cannot get a msg, then we'll wait
+        * When a messages are available, the int handler will wake_up()
+        * any waiters.
+        */
+       while (msg == NULL) {
+               pr_debug("%s:%d no available msg in VQ, waiting...\n",
+                      __FUNCTION__, __LINE__);
+               init_waitqueue_entry(&__wait, current);
+               add_wait_queue(&c2dev->req_vq_wo, &__wait);
+               spin_unlock(&c2dev->vqlock);
+               for (;;) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       if (!c2_mq_full(&c2dev->req_vq)) {
+                               break;
+                       }
+                       if (!signal_pending(current)) {
+                               schedule_timeout(1 * HZ);       /* 1 second... */
+                               continue;
+                       }
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&c2dev->req_vq_wo, &__wait);
+                       return -EINTR;
+               }
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&c2dev->req_vq_wo, &__wait);
+               spin_lock(&c2dev->vqlock);
+               msg = c2_mq_alloc(&c2dev->req_vq);
+       }
+
+       /*
+        * copy wr into adapter msg
+        */
+       memcpy(msg, wr, c2dev->req_vq.msg_size);
+
+       /*
+        * post msg
+        */
+       c2_mq_produce(&c2dev->req_vq);
+
+       /*
+        * release adapter vq lock
+        */
+       spin_unlock(&c2dev->vqlock);
+       return 0;
+}
+
+
+/*
+ * vq_wait_for_reply - block until the adapter posts a Verb Reply Message.
+ */
+int vq_wait_for_reply(struct c2_dev *c2dev, struct c2_vq_req *req)
+{
+       if (!wait_event_timeout(req->wait_object,
+                               atomic_read(&req->reply_ready),
+                               60*HZ))
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+/*
+ * vq_repbuf_free - Free a Verbs Reply Buffer.
+ */
+void vq_repbuf_free(struct c2_dev *c2dev, void *reply)
+{
+       kmem_cache_free(c2dev->host_msg_cache, reply);
+}
diff --git a/drivers/infiniband/hw/amso1100/c2_vq.h b/drivers/infiniband/hw/amso1100/c2_vq.h
new file mode 100644 (file)
index 0000000..3380562
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 _C2_VQ_H_
+#define _C2_VQ_H_
+#include <linux/sched.h>
+#include "c2.h"
+#include "c2_wr.h"
+#include "c2_provider.h"
+
+struct c2_vq_req {
+       u64 reply_msg;          /* ptr to reply msg */
+       wait_queue_head_t wait_object;  /* wait object for vq reqs */
+       atomic_t reply_ready;   /* set when reply is ready */
+       atomic_t refcnt;        /* used to cancel WRs... */
+       int event;
+       struct iw_cm_id *cm_id;
+       struct c2_qp *qp;
+};
+
+extern int vq_init(struct c2_dev *c2dev);
+extern void vq_term(struct c2_dev *c2dev);
+
+extern struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev);
+extern void vq_req_free(struct c2_dev *c2dev, struct c2_vq_req *req);
+extern void vq_req_get(struct c2_dev *c2dev, struct c2_vq_req *req);
+extern void vq_req_put(struct c2_dev *c2dev, struct c2_vq_req *req);
+extern int vq_send_wr(struct c2_dev *c2dev, union c2wr * wr);
+
+extern void *vq_repbuf_alloc(struct c2_dev *c2dev);
+extern void vq_repbuf_free(struct c2_dev *c2dev, void *reply);
+
+extern int vq_wait_for_reply(struct c2_dev *c2dev, struct c2_vq_req *req);
+#endif                         /* _C2_VQ_H_ */
diff --git a/drivers/infiniband/hw/amso1100/c2_wr.h b/drivers/infiniband/hw/amso1100/c2_wr.h
new file mode 100644 (file)
index 0000000..3ec6c43
--- /dev/null
@@ -0,0 +1,1520 @@
+/*
+ * Copyright (c) 2005 Ammasso, 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 _C2_WR_H_
+#define _C2_WR_H_
+
+#ifdef CCDEBUG
+#define CCWR_MAGIC             0xb07700b0
+#endif
+
+#define C2_QP_NO_ATTR_CHANGE 0xFFFFFFFF
+
+/* Maximum allowed size in bytes of private_data exchange
+ * on connect.
+ */
+#define C2_MAX_PRIVATE_DATA_SIZE 200
+
+/*
+ * These types are shared among the adapter, host, and CCIL consumer.
+ */
+enum c2_cq_notification_type {
+       C2_CQ_NOTIFICATION_TYPE_NONE = 1,
+       C2_CQ_NOTIFICATION_TYPE_NEXT,
+       C2_CQ_NOTIFICATION_TYPE_NEXT_SE
+};
+
+enum c2_setconfig_cmd {
+       C2_CFG_ADD_ADDR = 1,
+       C2_CFG_DEL_ADDR = 2,
+       C2_CFG_ADD_ROUTE = 3,
+       C2_CFG_DEL_ROUTE = 4
+};
+
+enum c2_getconfig_cmd {
+       C2_GETCONFIG_ROUTES = 1,
+       C2_GETCONFIG_ADDRS
+};
+
+/*
+ *  CCIL Work Request Identifiers
+ */
+enum c2wr_ids {
+       CCWR_RNIC_OPEN = 1,
+       CCWR_RNIC_QUERY,
+       CCWR_RNIC_SETCONFIG,
+       CCWR_RNIC_GETCONFIG,
+       CCWR_RNIC_CLOSE,
+       CCWR_CQ_CREATE,
+       CCWR_CQ_QUERY,
+       CCWR_CQ_MODIFY,
+       CCWR_CQ_DESTROY,
+       CCWR_QP_CONNECT,
+       CCWR_PD_ALLOC,
+       CCWR_PD_DEALLOC,
+       CCWR_SRQ_CREATE,
+       CCWR_SRQ_QUERY,
+       CCWR_SRQ_MODIFY,
+       CCWR_SRQ_DESTROY,
+       CCWR_QP_CREATE,
+       CCWR_QP_QUERY,
+       CCWR_QP_MODIFY,
+       CCWR_QP_DESTROY,
+       CCWR_NSMR_STAG_ALLOC,
+       CCWR_NSMR_REGISTER,
+       CCWR_NSMR_PBL,
+       CCWR_STAG_DEALLOC,
+       CCWR_NSMR_REREGISTER,
+       CCWR_SMR_REGISTER,
+       CCWR_MR_QUERY,
+       CCWR_MW_ALLOC,
+       CCWR_MW_QUERY,
+       CCWR_EP_CREATE,
+       CCWR_EP_GETOPT,
+       CCWR_EP_SETOPT,
+       CCWR_EP_DESTROY,
+       CCWR_EP_BIND,
+       CCWR_EP_CONNECT,
+       CCWR_EP_LISTEN,
+       CCWR_EP_SHUTDOWN,
+       CCWR_EP_LISTEN_CREATE,
+       CCWR_EP_LISTEN_DESTROY,
+       CCWR_EP_QUERY,
+       CCWR_CR_ACCEPT,
+       CCWR_CR_REJECT,
+       CCWR_CONSOLE,
+       CCWR_TERM,
+       CCWR_FLASH_INIT,
+       CCWR_FLASH,
+       CCWR_BUF_ALLOC,
+       CCWR_BUF_FREE,
+       CCWR_FLASH_WRITE,
+       CCWR_INIT,              /* WARNING: Don't move this ever again! */
+
+
+
+       /* Add new IDs here */
+
+
+
+       /*
+        * WARNING: CCWR_LAST must always be the last verbs id defined!
+        *          All the preceding IDs are fixed, and must not change.
+        *          You can add new IDs, but must not remove or reorder
+        *          any IDs. If you do, YOU will ruin any hope of
+        *          compatability between versions.
+        */
+       CCWR_LAST,
+
+       /*
+        * Start over at 1 so that arrays indexed by user wr id's
+        * begin at 1.  This is OK since the verbs and user wr id's
+        * are always used on disjoint sets of queues.
+        */
+       /*
+        * The order of the CCWR_SEND_XX verbs must
+        * match the order of the RDMA_OPs
+        */
+       CCWR_SEND = 1,
+       CCWR_SEND_INV,
+       CCWR_SEND_SE,
+       CCWR_SEND_SE_INV,
+       CCWR_RDMA_WRITE,
+       CCWR_RDMA_READ,
+       CCWR_RDMA_READ_INV,
+       CCWR_MW_BIND,
+       CCWR_NSMR_FASTREG,
+       CCWR_STAG_INVALIDATE,
+       CCWR_RECV,
+       CCWR_NOP,
+       CCWR_UNIMPL,
+/* WARNING: This must always be the last user wr id defined! */
+};
+#define RDMA_SEND_OPCODE_FROM_WR_ID(x)   (x+2)
+
+/*
+ * SQ/RQ Work Request Types
+ */
+enum c2_wr_type {
+       C2_WR_TYPE_SEND = CCWR_SEND,
+       C2_WR_TYPE_SEND_SE = CCWR_SEND_SE,
+       C2_WR_TYPE_SEND_INV = CCWR_SEND_INV,
+       C2_WR_TYPE_SEND_SE_INV = CCWR_SEND_SE_INV,
+       C2_WR_TYPE_RDMA_WRITE = CCWR_RDMA_WRITE,
+       C2_WR_TYPE_RDMA_READ = CCWR_RDMA_READ,
+       C2_WR_TYPE_RDMA_READ_INV_STAG = CCWR_RDMA_READ_INV,
+       C2_WR_TYPE_BIND_MW = CCWR_MW_BIND,
+       C2_WR_TYPE_FASTREG_NSMR = CCWR_NSMR_FASTREG,
+       C2_WR_TYPE_INV_STAG = CCWR_STAG_INVALIDATE,
+       C2_WR_TYPE_RECV = CCWR_RECV,
+       C2_WR_TYPE_NOP = CCWR_NOP,
+};
+
+struct c2_netaddr {
+       u32 ip_addr;
+       u32 netmask;
+       u32 mtu;
+};
+
+struct c2_route {
+       u32 ip_addr;            /* 0 indicates the default route */
+       u32 netmask;            /* netmask associated with dst */
+       u32 flags;
+       union {
+               u32 ipaddr;     /* address of the nexthop interface */
+               u8 enaddr[6];
+       } nexthop;
+};
+
+/*
+ * A Scatter Gather Entry.
+ */
+struct c2_data_addr {
+       u32 stag;
+       u32 length;
+       u64 to;
+};
+
+/*
+ * MR and MW flags used by the consumer, RI, and RNIC.
+ */
+enum c2_mm_flags {
+       MEM_REMOTE = 0x0001,    /* allow mw binds with remote access. */
+       MEM_VA_BASED = 0x0002,  /* Not Zero-based */
+       MEM_PBL_COMPLETE = 0x0004,      /* PBL array is complete in this msg */
+       MEM_LOCAL_READ = 0x0008,        /* allow local reads */
+       MEM_LOCAL_WRITE = 0x0010,       /* allow local writes */
+       MEM_REMOTE_READ = 0x0020,       /* allow remote reads */
+       MEM_REMOTE_WRITE = 0x0040,      /* allow remote writes */
+       MEM_WINDOW_BIND = 0x0080,       /* binds allowed */
+       MEM_SHARED = 0x0100,    /* set if MR is shared */
+       MEM_STAG_VALID = 0x0200 /* set if STAG is in valid state */
+};
+
+/*
+ * CCIL API ACF flags defined in terms of the low level mem flags.
+ * This minimizes translation needed in the user API
+ */
+enum c2_acf {
+       C2_ACF_LOCAL_READ = MEM_LOCAL_READ,
+       C2_ACF_LOCAL_WRITE = MEM_LOCAL_WRITE,
+       C2_ACF_REMOTE_READ = MEM_REMOTE_READ,
+       C2_ACF_REMOTE_WRITE = MEM_REMOTE_WRITE,
+       C2_ACF_WINDOW_BIND = MEM_WINDOW_BIND
+};
+
+/*
+ * Image types of objects written to flash
+ */
+#define C2_FLASH_IMG_BITFILE 1
+#define C2_FLASH_IMG_OPTION_ROM 2
+#define C2_FLASH_IMG_VPD 3
+
+/*
+ *  to fix bug 1815 we define the max size allowable of the
+ *  terminate message (per the IETF spec).Refer to the IETF
+ *  protocal specification, section 12.1.6, page 64)
+ *  The message is prefixed by 20 types of DDP info.
+ *
+ *  Then the message has 6 bytes for the terminate control
+ *  and DDP segment length info plus a DDP header (either
+ *  14 or 18 byts) plus 28 bytes for the RDMA header.
+ *  Thus the max size in:
+ *  20 + (6 + 18 + 28) = 72
+ */
+#define C2_MAX_TERMINATE_MESSAGE_SIZE (72)
+
+/*
+ * Build String Length.  It must be the same as C2_BUILD_STR_LEN in ccil_api.h
+ */
+#define WR_BUILD_STR_LEN 64
+
+/*
+ * WARNING:  All of these structs need to align any 64bit types on
+ * 64 bit boundaries!  64bit types include u64 and u64.
+ */
+
+/*
+ * Clustercore Work Request Header.  Be sensitive to field layout
+ * and alignment.
+ */
+struct c2wr_hdr {
+       /* wqe_count is part of the cqe.  It is put here so the
+        * adapter can write to it while the wr is pending without
+        * clobbering part of the wr.  This word need not be dma'd
+        * from the host to adapter by libccil, but we copy it anyway
+        * to make the memcpy to the adapter better aligned.
+        */
+       u32 wqe_count;
+
+       /* Put these fields next so that later 32- and 64-bit
+        * quantities are naturally aligned.
+        */
+       u8 id;
+       u8 result;              /* adapter -> host */
+       u8 sge_count;           /* host -> adapter */
+       u8 flags;               /* host -> adapter */
+
+       u64 context;
+#ifdef CCMSGMAGIC
+       u32 magic;
+       u32 pad;
+#endif
+} __attribute__((packed));
+
+/*
+ *------------------------ RNIC ------------------------
+ */
+
+/*
+ * WR_RNIC_OPEN
+ */
+
+/*
+ * Flags for the RNIC WRs
+ */
+enum c2_rnic_flags {
+       RNIC_IRD_STATIC = 0x0001,
+       RNIC_ORD_STATIC = 0x0002,
+       RNIC_QP_STATIC = 0x0004,
+       RNIC_SRQ_SUPPORTED = 0x0008,
+       RNIC_PBL_BLOCK_MODE = 0x0010,
+       RNIC_SRQ_MODEL_ARRIVAL = 0x0020,
+       RNIC_CQ_OVF_DETECTED = 0x0040,
+       RNIC_PRIV_MODE = 0x0080
+};
+
+struct c2wr_rnic_open_req {
+       struct c2wr_hdr hdr;
+       u64 user_context;
+       u16 flags;              /* See enum c2_rnic_flags */
+       u16 port_num;
+} __attribute__((packed));
+
+struct c2wr_rnic_open_rep {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+} __attribute__((packed));
+
+union c2wr_rnic_open {
+       struct c2wr_rnic_open_req req;
+       struct c2wr_rnic_open_rep rep;
+} __attribute__((packed));
+
+struct c2wr_rnic_query_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+} __attribute__((packed));
+
+/*
+ * WR_RNIC_QUERY
+ */
+struct c2wr_rnic_query_rep {
+       struct c2wr_hdr hdr;
+       u64 user_context;
+       u32 vendor_id;
+       u32 part_number;
+       u32 hw_version;
+       u32 fw_ver_major;
+       u32 fw_ver_minor;
+       u32 fw_ver_patch;
+       char fw_ver_build_str[WR_BUILD_STR_LEN];
+       u32 max_qps;
+       u32 max_qp_depth;
+       u32 max_srq_depth;
+       u32 max_send_sgl_depth;
+       u32 max_rdma_sgl_depth;
+       u32 max_cqs;
+       u32 max_cq_depth;
+       u32 max_cq_event_handlers;
+       u32 max_mrs;
+       u32 max_pbl_depth;
+       u32 max_pds;
+       u32 max_global_ird;
+       u32 max_global_ord;
+       u32 max_qp_ird;
+       u32 max_qp_ord;
+       u32 flags;
+       u32 max_mws;
+       u32 pbe_range_low;
+       u32 pbe_range_high;
+       u32 max_srqs;
+       u32 page_size;
+} __attribute__((packed));
+
+union c2wr_rnic_query {
+       struct c2wr_rnic_query_req req;
+       struct c2wr_rnic_query_rep rep;
+} __attribute__((packed));
+
+/*
+ * WR_RNIC_GETCONFIG
+ */
+
+struct c2wr_rnic_getconfig_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 option;             /* see c2_getconfig_cmd_t */
+       u64 reply_buf;
+       u32 reply_buf_len;
+} __attribute__((packed)) ;
+
+struct c2wr_rnic_getconfig_rep {
+       struct c2wr_hdr hdr;
+       u32 option;             /* see c2_getconfig_cmd_t */
+       u32 count_len;          /* length of the number of addresses configured */
+} __attribute__((packed)) ;
+
+union c2wr_rnic_getconfig {
+       struct c2wr_rnic_getconfig_req req;
+       struct c2wr_rnic_getconfig_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ * WR_RNIC_SETCONFIG
+ */
+struct c2wr_rnic_setconfig_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 option;             /* See c2_setconfig_cmd_t */
+       /* variable data and pad. See c2_netaddr and c2_route */
+       u8 data[0];
+} __attribute__((packed)) ;
+
+struct c2wr_rnic_setconfig_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_rnic_setconfig {
+       struct c2wr_rnic_setconfig_req req;
+       struct c2wr_rnic_setconfig_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ * WR_RNIC_CLOSE
+ */
+struct c2wr_rnic_close_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+} __attribute__((packed)) ;
+
+struct c2wr_rnic_close_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_rnic_close {
+       struct c2wr_rnic_close_req req;
+       struct c2wr_rnic_close_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ *------------------------ CQ ------------------------
+ */
+struct c2wr_cq_create_req {
+       struct c2wr_hdr hdr;
+       u64 shared_ht;
+       u64 user_context;
+       u64 msg_pool;
+       u32 rnic_handle;
+       u32 msg_size;
+       u32 depth;
+} __attribute__((packed)) ;
+
+struct c2wr_cq_create_rep {
+       struct c2wr_hdr hdr;
+       u32 mq_index;
+       u32 adapter_shared;
+       u32 cq_handle;
+} __attribute__((packed)) ;
+
+union c2wr_cq_create {
+       struct c2wr_cq_create_req req;
+       struct c2wr_cq_create_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_cq_modify_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 cq_handle;
+       u32 new_depth;
+       u64 new_msg_pool;
+} __attribute__((packed)) ;
+
+struct c2wr_cq_modify_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_cq_modify {
+       struct c2wr_cq_modify_req req;
+       struct c2wr_cq_modify_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_cq_destroy_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 cq_handle;
+} __attribute__((packed)) ;
+
+struct c2wr_cq_destroy_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_cq_destroy {
+       struct c2wr_cq_destroy_req req;
+       struct c2wr_cq_destroy_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ *------------------------ PD ------------------------
+ */
+struct c2wr_pd_alloc_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 pd_id;
+} __attribute__((packed)) ;
+
+struct c2wr_pd_alloc_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_pd_alloc {
+       struct c2wr_pd_alloc_req req;
+       struct c2wr_pd_alloc_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_pd_dealloc_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 pd_id;
+} __attribute__((packed)) ;
+
+struct c2wr_pd_dealloc_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_pd_dealloc {
+       struct c2wr_pd_dealloc_req req;
+       struct c2wr_pd_dealloc_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ *------------------------ SRQ ------------------------
+ */
+struct c2wr_srq_create_req {
+       struct c2wr_hdr hdr;
+       u64 shared_ht;
+       u64 user_context;
+       u32 rnic_handle;
+       u32 srq_depth;
+       u32 srq_limit;
+       u32 sgl_depth;
+       u32 pd_id;
+} __attribute__((packed)) ;
+
+struct c2wr_srq_create_rep {
+       struct c2wr_hdr hdr;
+       u32 srq_depth;
+       u32 sgl_depth;
+       u32 msg_size;
+       u32 mq_index;
+       u32 mq_start;
+       u32 srq_handle;
+} __attribute__((packed)) ;
+
+union c2wr_srq_create {
+       struct c2wr_srq_create_req req;
+       struct c2wr_srq_create_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_srq_destroy_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 srq_handle;
+} __attribute__((packed)) ;
+
+struct c2wr_srq_destroy_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_srq_destroy {
+       struct c2wr_srq_destroy_req req;
+       struct c2wr_srq_destroy_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ *------------------------ QP ------------------------
+ */
+enum c2wr_qp_flags {
+       QP_RDMA_READ = 0x00000001,      /* RDMA read enabled? */
+       QP_RDMA_WRITE = 0x00000002,     /* RDMA write enabled? */
+       QP_MW_BIND = 0x00000004,        /* MWs enabled */
+       QP_ZERO_STAG = 0x00000008,      /* enabled? */
+       QP_REMOTE_TERMINATION = 0x00000010,     /* remote end terminated */
+       QP_RDMA_READ_RESPONSE = 0x00000020      /* Remote RDMA read  */
+           /* enabled? */
+};
+
+struct c2wr_qp_create_req {
+       struct c2wr_hdr hdr;
+       u64 shared_sq_ht;
+       u64 shared_rq_ht;
+       u64 user_context;
+       u32 rnic_handle;
+       u32 sq_cq_handle;
+       u32 rq_cq_handle;
+       u32 sq_depth;
+       u32 rq_depth;
+       u32 srq_handle;
+       u32 srq_limit;
+       u32 flags;              /* see enum c2wr_qp_flags */
+       u32 send_sgl_depth;
+       u32 recv_sgl_depth;
+       u32 rdma_write_sgl_depth;
+       u32 ord;
+       u32 ird;
+       u32 pd_id;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_create_rep {
+       struct c2wr_hdr hdr;
+       u32 sq_depth;
+       u32 rq_depth;
+       u32 send_sgl_depth;
+       u32 recv_sgl_depth;
+       u32 rdma_write_sgl_depth;
+       u32 ord;
+       u32 ird;
+       u32 sq_msg_size;
+       u32 sq_mq_index;
+       u32 sq_mq_start;
+       u32 rq_msg_size;
+       u32 rq_mq_index;
+       u32 rq_mq_start;
+       u32 qp_handle;
+} __attribute__((packed)) ;
+
+union c2wr_qp_create {
+       struct c2wr_qp_create_req req;
+       struct c2wr_qp_create_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_query_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 qp_handle;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_query_rep {
+       struct c2wr_hdr hdr;
+       u64 user_context;
+       u32 rnic_handle;
+       u32 sq_depth;
+       u32 rq_depth;
+       u32 send_sgl_depth;
+       u32 rdma_write_sgl_depth;
+       u32 recv_sgl_depth;
+       u32 ord;
+       u32 ird;
+       u16 qp_state;
+       u16 flags;              /* see c2wr_qp_flags_t */
+       u32 qp_id;
+       u32 local_addr;
+       u32 remote_addr;
+       u16 local_port;
+       u16 remote_port;
+       u32 terminate_msg_length;       /* 0 if not present */
+       u8 data[0];
+       /* Terminate Message in-line here. */
+} __attribute__((packed)) ;
+
+union c2wr_qp_query {
+       struct c2wr_qp_query_req req;
+       struct c2wr_qp_query_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_modify_req {
+       struct c2wr_hdr hdr;
+       u64 stream_msg;
+       u32 stream_msg_length;
+       u32 rnic_handle;
+       u32 qp_handle;
+       u32 next_qp_state;
+       u32 ord;
+       u32 ird;
+       u32 sq_depth;
+       u32 rq_depth;
+       u32 llp_ep_handle;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_modify_rep {
+       struct c2wr_hdr hdr;
+       u32 ord;
+       u32 ird;
+       u32 sq_depth;
+       u32 rq_depth;
+       u32 sq_msg_size;
+       u32 sq_mq_index;
+       u32 sq_mq_start;
+       u32 rq_msg_size;
+       u32 rq_mq_index;
+       u32 rq_mq_start;
+} __attribute__((packed)) ;
+
+union c2wr_qp_modify {
+       struct c2wr_qp_modify_req req;
+       struct c2wr_qp_modify_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_destroy_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 qp_handle;
+} __attribute__((packed)) ;
+
+struct c2wr_qp_destroy_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_qp_destroy {
+       struct c2wr_qp_destroy_req req;
+       struct c2wr_qp_destroy_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ * The CCWR_QP_CONNECT msg is posted on the verbs request queue.  It can
+ * only be posted when a QP is in IDLE state.  After the connect request is
+ * submitted to the LLP, the adapter moves the QP to CONNECT_PENDING state.
+ * No synchronous reply from adapter to this WR.  The results of
+ * connection are passed back in an async event CCAE_ACTIVE_CONNECT_RESULTS
+ * See c2wr_ae_active_connect_results_t
+ */
+struct c2wr_qp_connect_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 qp_handle;
+       u32 remote_addr;
+       u16 remote_port;
+       u16 pad;
+       u32 private_data_length;
+       u8 private_data[0];     /* Private data in-line. */
+} __attribute__((packed)) ;
+
+struct c2wr_qp_connect {
+       struct c2wr_qp_connect_req req;
+       /* no synchronous reply.         */
+} __attribute__((packed)) ;
+
+
+/*
+ *------------------------ MM ------------------------
+ */
+
+struct c2wr_nsmr_stag_alloc_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 pbl_depth;
+       u32 pd_id;
+       u32 flags;
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_stag_alloc_rep {
+       struct c2wr_hdr hdr;
+       u32 pbl_depth;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+union c2wr_nsmr_stag_alloc {
+       struct c2wr_nsmr_stag_alloc_req req;
+       struct c2wr_nsmr_stag_alloc_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_register_req {
+       struct c2wr_hdr hdr;
+       u64 va;
+       u32 rnic_handle;
+       u16 flags;
+       u8 stag_key;
+       u8 pad;
+       u32 pd_id;
+       u32 pbl_depth;
+       u32 pbe_size;
+       u32 fbo;
+       u32 length;
+       u32 addrs_length;
+       /* array of paddrs (must be aligned on a 64bit boundary) */
+       u64 paddrs[0];
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_register_rep {
+       struct c2wr_hdr hdr;
+       u32 pbl_depth;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+union c2wr_nsmr_register {
+       struct c2wr_nsmr_register_req req;
+       struct c2wr_nsmr_register_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_pbl_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 flags;
+       u32 stag_index;
+       u32 addrs_length;
+       /* array of paddrs (must be aligned on a 64bit boundary) */
+       u64 paddrs[0];
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_pbl_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_nsmr_pbl {
+       struct c2wr_nsmr_pbl_req req;
+       struct c2wr_nsmr_pbl_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_mr_query_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+struct c2wr_mr_query_rep {
+       struct c2wr_hdr hdr;
+       u8 stag_key;
+       u8 pad[3];
+       u32 pd_id;
+       u32 flags;
+       u32 pbl_depth;
+} __attribute__((packed)) ;
+
+union c2wr_mr_query {
+       struct c2wr_mr_query_req req;
+       struct c2wr_mr_query_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_mw_query_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+struct c2wr_mw_query_rep {
+       struct c2wr_hdr hdr;
+       u8 stag_key;
+       u8 pad[3];
+       u32 pd_id;
+       u32 flags;
+} __attribute__((packed)) ;
+
+union c2wr_mw_query {
+       struct c2wr_mw_query_req req;
+       struct c2wr_mw_query_rep rep;
+} __attribute__((packed)) ;
+
+
+struct c2wr_stag_dealloc_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+struct c2wr_stag_dealloc_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed)) ;
+
+union c2wr_stag_dealloc {
+       struct c2wr_stag_dealloc_req req;
+       struct c2wr_stag_dealloc_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_reregister_req {
+       struct c2wr_hdr hdr;
+       u64 va;
+       u32 rnic_handle;
+       u16 flags;
+       u8 stag_key;
+       u8 pad;
+       u32 stag_index;
+       u32 pd_id;
+       u32 pbl_depth;
+       u32 pbe_size;
+       u32 fbo;
+       u32 length;
+       u32 addrs_length;
+       u32 pad1;
+       /* array of paddrs (must be aligned on a 64bit boundary) */
+       u64 paddrs[0];
+} __attribute__((packed)) ;
+
+struct c2wr_nsmr_reregister_rep {
+       struct c2wr_hdr hdr;
+       u32 pbl_depth;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+union c2wr_nsmr_reregister {
+       struct c2wr_nsmr_reregister_req req;
+       struct c2wr_nsmr_reregister_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_smr_register_req {
+       struct c2wr_hdr hdr;
+       u64 va;
+       u32 rnic_handle;
+       u16 flags;
+       u8 stag_key;
+       u8 pad;
+       u32 stag_index;
+       u32 pd_id;
+} __attribute__((packed)) ;
+
+struct c2wr_smr_register_rep {
+       struct c2wr_hdr hdr;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+union c2wr_smr_register {
+       struct c2wr_smr_register_req req;
+       struct c2wr_smr_register_rep rep;
+} __attribute__((packed)) ;
+
+struct c2wr_mw_alloc_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 pd_id;
+} __attribute__((packed)) ;
+
+struct c2wr_mw_alloc_rep {
+       struct c2wr_hdr hdr;
+       u32 stag_index;
+} __attribute__((packed)) ;
+
+union c2wr_mw_alloc {
+       struct c2wr_mw_alloc_req req;
+       struct c2wr_mw_alloc_rep rep;
+} __attribute__((packed)) ;
+
+/*
+ *------------------------ WRs -----------------------
+ */
+
+struct c2wr_user_hdr {
+       struct c2wr_hdr hdr;            /* Has status and WR Type */
+} __attribute__((packed)) ;
+
+enum c2_qp_state {
+       C2_QP_STATE_IDLE = 0x01,
+       C2_QP_STATE_CONNECTING = 0x02,
+       C2_QP_STATE_RTS = 0x04,
+       C2_QP_STATE_CLOSING = 0x08,
+       C2_QP_STATE_TERMINATE = 0x10,
+       C2_QP_STATE_ERROR = 0x20,
+};
+
+/* Completion queue entry. */
+struct c2wr_ce {
+       struct c2wr_hdr hdr;            /* Has status and WR Type */
+       u64 qp_user_context;    /* c2_user_qp_t * */
+       u32 qp_state;           /* Current QP State */
+       u32 handle;             /* QPID or EP Handle */
+       u32 bytes_rcvd;         /* valid for RECV WCs */
+       u32 stag;
+} __attribute__((packed)) ;
+
+
+/*
+ * Flags used for all post-sq WRs.  These must fit in the flags
+ * field of the struct c2wr_hdr (eight bits).
+ */
+enum {
+       SQ_SIGNALED = 0x01,
+       SQ_READ_FENCE = 0x02,
+       SQ_FENCE = 0x04,
+};
+
+/*
+ * Common fields for all post-sq WRs.  Namely the standard header and a
+ * secondary header with fields common to all post-sq WRs.
+ */
+struct c2_sq_hdr {
+       struct c2wr_user_hdr user_hdr;
+} __attribute__((packed));
+
+/*
+ * Same as above but for post-rq WRs.
+ */
+struct c2_rq_hdr {
+       struct c2wr_user_hdr user_hdr;
+} __attribute__((packed));
+
+/*
+ * use the same struct for all sends.
+ */
+struct c2wr_send_req {
+       struct c2_sq_hdr sq_hdr;
+       u32 sge_len;
+       u32 remote_stag;
+       u8 data[0];             /* SGE array */
+} __attribute__((packed));
+
+union c2wr_send {
+       struct c2wr_send_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+struct c2wr_rdma_write_req {
+       struct c2_sq_hdr sq_hdr;
+       u64 remote_to;
+       u32 remote_stag;
+       u32 sge_len;
+       u8 data[0];             /* SGE array */
+} __attribute__((packed));
+
+union c2wr_rdma_write {
+       struct c2wr_rdma_write_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+struct c2wr_rdma_read_req {
+       struct c2_sq_hdr sq_hdr;
+       u64 local_to;
+       u64 remote_to;
+       u32 local_stag;
+       u32 remote_stag;
+       u32 length;
+} __attribute__((packed));
+
+union c2wr_rdma_read {
+       struct c2wr_rdma_read_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+struct c2wr_mw_bind_req {
+       struct c2_sq_hdr sq_hdr;
+       u64 va;
+       u8 stag_key;
+       u8 pad[3];
+       u32 mw_stag_index;
+       u32 mr_stag_index;
+       u32 length;
+       u32 flags;
+} __attribute__((packed));
+
+union c2wr_mw_bind {
+       struct c2wr_mw_bind_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+struct c2wr_nsmr_fastreg_req {
+       struct c2_sq_hdr sq_hdr;
+       u64 va;
+       u8 stag_key;
+       u8 pad[3];
+       u32 stag_index;
+       u32 pbe_size;
+       u32 fbo;
+       u32 length;
+       u32 addrs_length;
+       /* array of paddrs (must be aligned on a 64bit boundary) */
+       u64 paddrs[0];
+} __attribute__((packed));
+
+union c2wr_nsmr_fastreg {
+       struct c2wr_nsmr_fastreg_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+struct c2wr_stag_invalidate_req {
+       struct c2_sq_hdr sq_hdr;
+       u8 stag_key;
+       u8 pad[3];
+       u32 stag_index;
+} __attribute__((packed));
+
+union c2wr_stag_invalidate {
+       struct c2wr_stag_invalidate_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+union c2wr_sqwr {
+       struct c2_sq_hdr sq_hdr;
+       struct c2wr_send_req send;
+       struct c2wr_send_req send_se;
+       struct c2wr_send_req send_inv;
+       struct c2wr_send_req send_se_inv;
+       struct c2wr_rdma_write_req rdma_write;
+       struct c2wr_rdma_read_req rdma_read;
+       struct c2wr_mw_bind_req mw_bind;
+       struct c2wr_nsmr_fastreg_req nsmr_fastreg;
+       struct c2wr_stag_invalidate_req stag_inv;
+} __attribute__((packed));
+
+
+/*
+ * RQ WRs
+ */
+struct c2wr_rqwr {
+       struct c2_rq_hdr rq_hdr;
+       u8 data[0];             /* array of SGEs */
+} __attribute__((packed));
+
+union c2wr_recv {
+       struct c2wr_rqwr req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+/*
+ * All AEs start with this header.  Most AEs only need to convey the
+ * information in the header.  Some, like LLP connection events, need
+ * more info.  The union typdef c2wr_ae_t has all the possible AEs.
+ *
+ * hdr.context is the user_context from the rnic_open WR.  NULL If this
+ * is not affiliated with an rnic
+ *
+ * hdr.id is the AE identifier (eg;  CCAE_REMOTE_SHUTDOWN,
+ * CCAE_LLP_CLOSE_COMPLETE)
+ *
+ * resource_type is one of:  C2_RES_IND_QP, C2_RES_IND_CQ, C2_RES_IND_SRQ
+ *
+ * user_context is the context passed down when the host created the resource.
+ */
+struct c2wr_ae_hdr {
+       struct c2wr_hdr hdr;
+       u64 user_context;       /* user context for this res. */
+       u32 resource_type;      /* see enum c2_resource_indicator */
+       u32 resource;           /* handle for resource */
+       u32 qp_state;           /* current QP State */
+} __attribute__((packed));
+
+/*
+ * After submitting the CCAE_ACTIVE_CONNECT_RESULTS message on the AEQ,
+ * the adapter moves the QP into RTS state
+ */
+struct c2wr_ae_active_connect_results {
+       struct c2wr_ae_hdr ae_hdr;
+       u32 laddr;
+       u32 raddr;
+       u16 lport;
+       u16 rport;
+       u32 private_data_length;
+       u8 private_data[0];     /* data is in-line in the msg. */
+} __attribute__((packed));
+
+/*
+ * When connections are established by the stack (and the private data
+ * MPA frame is received), the adapter will generate an event to the host.
+ * The details of the connection, any private data, and the new connection
+ * request handle is passed up via the CCAE_CONNECTION_REQUEST msg on the
+ * AE queue:
+ */
+struct c2wr_ae_connection_request {
+       struct c2wr_ae_hdr ae_hdr;
+       u32 cr_handle;          /* connreq handle (sock ptr) */
+       u32 laddr;
+       u32 raddr;
+       u16 lport;
+       u16 rport;
+       u32 private_data_length;
+       u8 private_data[0];     /* data is in-line in the msg. */
+} __attribute__((packed));
+
+union c2wr_ae {
+       struct c2wr_ae_hdr ae_generic;
+       struct c2wr_ae_active_connect_results ae_active_connect_results;
+       struct c2wr_ae_connection_request ae_connection_request;
+} __attribute__((packed));
+
+struct c2wr_init_req {
+       struct c2wr_hdr hdr;
+       u64 hint_count;
+       u64 q0_host_shared;
+       u64 q1_host_shared;
+       u64 q1_host_msg_pool;
+       u64 q2_host_shared;
+       u64 q2_host_msg_pool;
+} __attribute__((packed));
+
+struct c2wr_init_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed));
+
+union c2wr_init {
+       struct c2wr_init_req req;
+       struct c2wr_init_rep rep;
+} __attribute__((packed));
+
+/*
+ * For upgrading flash.
+ */
+
+struct c2wr_flash_init_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+} __attribute__((packed));
+
+struct c2wr_flash_init_rep {
+       struct c2wr_hdr hdr;
+       u32 adapter_flash_buf_offset;
+       u32 adapter_flash_len;
+} __attribute__((packed));
+
+union c2wr_flash_init {
+       struct c2wr_flash_init_req req;
+       struct c2wr_flash_init_rep rep;
+} __attribute__((packed));
+
+struct c2wr_flash_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 len;
+} __attribute__((packed));
+
+struct c2wr_flash_rep {
+       struct c2wr_hdr hdr;
+       u32 status;
+} __attribute__((packed));
+
+union c2wr_flash {
+       struct c2wr_flash_req req;
+       struct c2wr_flash_rep rep;
+} __attribute__((packed));
+
+struct c2wr_buf_alloc_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 size;
+} __attribute__((packed));
+
+struct c2wr_buf_alloc_rep {
+       struct c2wr_hdr hdr;
+       u32 offset;             /* 0 if mem not available */
+       u32 size;               /* 0 if mem not available */
+} __attribute__((packed));
+
+union c2wr_buf_alloc {
+       struct c2wr_buf_alloc_req req;
+       struct c2wr_buf_alloc_rep rep;
+} __attribute__((packed));
+
+struct c2wr_buf_free_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 offset;             /* Must match value from alloc */
+       u32 size;               /* Must match value from alloc */
+} __attribute__((packed));
+
+struct c2wr_buf_free_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed));
+
+union c2wr_buf_free {
+       struct c2wr_buf_free_req req;
+       struct c2wr_ce rep;
+} __attribute__((packed));
+
+struct c2wr_flash_write_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 offset;
+       u32 size;
+       u32 type;
+       u32 flags;
+} __attribute__((packed));
+
+struct c2wr_flash_write_rep {
+       struct c2wr_hdr hdr;
+       u32 status;
+} __attribute__((packed));
+
+union c2wr_flash_write {
+       struct c2wr_flash_write_req req;
+       struct c2wr_flash_write_rep rep;
+} __attribute__((packed));
+
+/*
+ * Messages for LLP connection setup.
+ */
+
+/*
+ * Listen Request.  This allocates a listening endpoint to allow passive
+ * connection setup.  Newly established LLP connections are passed up
+ * via an AE.  See c2wr_ae_connection_request_t
+ */
+struct c2wr_ep_listen_create_req {
+       struct c2wr_hdr hdr;
+       u64 user_context;       /* returned in AEs. */
+       u32 rnic_handle;
+       u32 local_addr;         /* local addr, or 0  */
+       u16 local_port;         /* 0 means "pick one" */
+       u16 pad;
+       u32 backlog;            /* tradional tcp listen bl */
+} __attribute__((packed));
+
+struct c2wr_ep_listen_create_rep {
+       struct c2wr_hdr hdr;
+       u32 ep_handle;          /* handle to new listening ep */
+       u16 local_port;         /* resulting port... */
+       u16 pad;
+} __attribute__((packed));
+
+union c2wr_ep_listen_create {
+       struct c2wr_ep_listen_create_req req;
+       struct c2wr_ep_listen_create_rep rep;
+} __attribute__((packed));
+
+struct c2wr_ep_listen_destroy_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 ep_handle;
+} __attribute__((packed));
+
+struct c2wr_ep_listen_destroy_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed));
+
+union c2wr_ep_listen_destroy {
+       struct c2wr_ep_listen_destroy_req req;
+       struct c2wr_ep_listen_destroy_rep rep;
+} __attribute__((packed));
+
+struct c2wr_ep_query_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 ep_handle;
+} __attribute__((packed));
+
+struct c2wr_ep_query_rep {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 local_addr;
+       u32 remote_addr;
+       u16 local_port;
+       u16 remote_port;
+} __attribute__((packed));
+
+union c2wr_ep_query {
+       struct c2wr_ep_query_req req;
+       struct c2wr_ep_query_rep rep;
+} __attribute__((packed));
+
+
+/*
+ * The host passes this down to indicate acceptance of a pending iWARP
+ * connection.  The cr_handle was obtained from the CONNECTION_REQUEST
+ * AE passed up by the adapter.  See c2wr_ae_connection_request_t.
+ */
+struct c2wr_cr_accept_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 qp_handle;          /* QP to bind to this LLP conn */
+       u32 ep_handle;          /* LLP  handle to accept */
+       u32 private_data_length;
+       u8 private_data[0];     /* data in-line in msg. */
+} __attribute__((packed));
+
+/*
+ * adapter sends reply when private data is successfully submitted to
+ * the LLP.
+ */
+struct c2wr_cr_accept_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed));
+
+union c2wr_cr_accept {
+       struct c2wr_cr_accept_req req;
+       struct c2wr_cr_accept_rep rep;
+} __attribute__((packed));
+
+/*
+ * The host sends this down if a given iWARP connection request was
+ * rejected by the consumer.  The cr_handle was obtained from a
+ * previous c2wr_ae_connection_request_t AE sent by the adapter.
+ */
+struct  c2wr_cr_reject_req {
+       struct c2wr_hdr hdr;
+       u32 rnic_handle;
+       u32 ep_handle;          /* LLP handle to reject */
+} __attribute__((packed));
+
+/*
+ * Dunno if this is needed, but we'll add it for now.  The adapter will
+ * send the reject_reply after the LLP endpoint has been destroyed.
+ */
+struct  c2wr_cr_reject_rep {
+       struct c2wr_hdr hdr;
+} __attribute__((packed));
+
+union c2wr_cr_reject {
+       struct c2wr_cr_reject_req req;
+       struct c2wr_cr_reject_rep rep;
+} __attribute__((packed));
+
+/*
+ * console command.  Used to implement a debug console over the verbs
+ * request and reply queues.
+ */
+
+/*
+ * Console request message.  It contains:
+ *     - message hdr with id = CCWR_CONSOLE
+ *     - the physaddr/len of host memory to be used for the reply.
+ *     - the command string.  eg:  "netstat -s" or "zoneinfo"
+ */
+struct c2wr_console_req {
+       struct c2wr_hdr hdr;            /* id = CCWR_CONSOLE */
+       u64 reply_buf;          /* pinned host buf for reply */
+       u32 reply_buf_len;      /* length of reply buffer */
+       u8 command[0];          /* NUL terminated ascii string */
+       /* containing the command req */
+} __attribute__((packed));
+
+/*
+ * flags used in the console reply.
+ */
+enum c2_console_flags {
+       CONS_REPLY_TRUNCATED = 0x00000001       /* reply was truncated */
+} __attribute__((packed));
+
+/*
+ * Console reply message.
+ * hdr.result contains the c2_status_t error if the reply was _not_ generated,
+ * or C2_OK if the reply was generated.
+ */
+struct c2wr_console_rep {
+       struct c2wr_hdr hdr;            /* id = CCWR_CONSOLE */
+       u32 flags;
+} __attribute__((packed));
+
+union c2wr_console {
+       struct c2wr_console_req req;
+       struct c2wr_console_rep rep;
+} __attribute__((packed));
+
+
+/*
+ * Giant union with all WRs.  Makes life easier...
+ */
+union c2wr {
+       struct c2wr_hdr hdr;
+       struct c2wr_user_hdr user_hdr;
+       union c2wr_rnic_open rnic_open;
+       union c2wr_rnic_query rnic_query;
+       union c2wr_rnic_getconfig rnic_getconfig;
+       union c2wr_rnic_setconfig rnic_setconfig;
+       union c2wr_rnic_close rnic_close;
+       union c2wr_cq_create cq_create;
+       union c2wr_cq_modify cq_modify;
+       union c2wr_cq_destroy cq_destroy;
+       union c2wr_pd_alloc pd_alloc;
+       union c2wr_pd_dealloc pd_dealloc;
+       union c2wr_srq_create srq_create;
+       union c2wr_srq_destroy srq_destroy;
+       union c2wr_qp_create qp_create;
+       union c2wr_qp_query qp_query;
+       union c2wr_qp_modify qp_modify;
+       union c2wr_qp_destroy qp_destroy;
+       struct c2wr_qp_connect qp_connect;
+       union c2wr_nsmr_stag_alloc nsmr_stag_alloc;
+       union c2wr_nsmr_register nsmr_register;
+       union c2wr_nsmr_pbl nsmr_pbl;
+       union c2wr_mr_query mr_query;
+       union c2wr_mw_query mw_query;
+       union c2wr_stag_dealloc stag_dealloc;
+       union c2wr_sqwr sqwr;
+       struct c2wr_rqwr rqwr;
+       struct c2wr_ce ce;
+       union c2wr_ae ae;
+       union c2wr_init init;
+       union c2wr_ep_listen_create ep_listen_create;
+       union c2wr_ep_listen_destroy ep_listen_destroy;
+       union c2wr_cr_accept cr_accept;
+       union c2wr_cr_reject cr_reject;
+       union c2wr_console console;
+       union c2wr_flash_init flash_init;
+       union c2wr_flash flash;
+       union c2wr_buf_alloc buf_alloc;
+       union c2wr_buf_free buf_free;
+       union c2wr_flash_write flash_write;
+} __attribute__((packed));
+
+
+/*
+ * Accessors for the wr fields that are packed together tightly to
+ * reduce the wr message size.  The wr arguments are void* so that
+ * either a struct c2wr*, a struct c2wr_hdr*, or a pointer to any of the types
+ * in the struct c2wr union can be passed in.
+ */
+static __inline__ u8 c2_wr_get_id(void *wr)
+{
+       return ((struct c2wr_hdr *) wr)->id;
+}
+static __inline__ void c2_wr_set_id(void *wr, u8 id)
+{
+       ((struct c2wr_hdr *) wr)->id = id;
+}
+static __inline__ u8 c2_wr_get_result(void *wr)
+{
+       return ((struct c2wr_hdr *) wr)->result;
+}
+static __inline__ void c2_wr_set_result(void *wr, u8 result)
+{
+       ((struct c2wr_hdr *) wr)->result = result;
+}
+static __inline__ u8 c2_wr_get_flags(void *wr)
+{
+       return ((struct c2wr_hdr *) wr)->flags;
+}
+static __inline__ void c2_wr_set_flags(void *wr, u8 flags)
+{
+       ((struct c2wr_hdr *) wr)->flags = flags;
+}
+static __inline__ u8 c2_wr_get_sge_count(void *wr)
+{
+       return ((struct c2wr_hdr *) wr)->sge_count;
+}
+static __inline__ void c2_wr_set_sge_count(void *wr, u8 sge_count)
+{
+       ((struct c2wr_hdr *) wr)->sge_count = sge_count;
+}
+static __inline__ u32 c2_wr_get_wqe_count(void *wr)
+{
+       return ((struct c2wr_hdr *) wr)->wqe_count;
+}
+static __inline__ void c2_wr_set_wqe_count(void *wr, u32 wqe_count)
+{
+       ((struct c2wr_hdr *) wr)->wqe_count = wqe_count;
+}
+
+#endif                         /* _C2_WR_H_ */