Commit f94b533d authored by Tom Tucker's avatar Tom Tucker Committed by Roland Dreier

RDMA/amso1100: Add driver for Ammasso 1100 RNIC

Add a driver for the Ammasso 1100 gigabit ethernet RNIC.
Signed-off-by: default avatarTom Tucker <tom@opengridcomputing.com>
Signed-off-by: default avatarSteve Wise <swise@opengridcomputing.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 07ebafba
......@@ -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
......
......@@ -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"
......
......@@ -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/
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
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.
/*
* 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++;
}