Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d22de3e authored by Divy Le Ray's avatar Divy Le Ray Committed by Jeff Garzik
Browse files

Add support for the latest 1G/10G Chelsio adapter, T3.



This driver is required by the Chelsio T3 RDMA driver posted by
Steve Wise.

Signed-off-by: default avatarDivy Le Ray <divy@chelsio.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 0bf94faf
Loading
Loading
Loading
Loading
+18 −0
Original line number Original line Diff line number Diff line
@@ -2389,6 +2389,24 @@ config CHELSIO_T1_NAPI
	  NAPI is a driver API designed to reduce CPU and interrupt load
	  NAPI is a driver API designed to reduce CPU and interrupt load
	  when the driver is receiving lots of packets from the card.
	  when the driver is receiving lots of packets from the card.


config CHELSIO_T3
        tristate "Chelsio Communications T3 10Gb Ethernet support"
        depends on PCI
        help
          This driver supports Chelsio T3-based gigabit and 10Gb Ethernet
          adapters.

          For general information about Chelsio and our products, visit
          our website at <http://www.chelsio.com>.

          For customer support, please visit our customer support page at
          <http://www.chelsio.com/support.htm>.

          Please send feedback to <linux-bugs@chelsio.com>.

          To compile this driver as a module, choose M here: the module
          will be called cxgb3.

config EHEA
config EHEA
	tristate "eHEA Ethernet support"
	tristate "eHEA Ethernet support"
	depends on IBMEBUS
	depends on IBMEBUS
+1 −0
Original line number Original line Diff line number Diff line
@@ -6,6 +6,7 @@ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_CHELSIO_T3) += cxgb3/
obj-$(CONFIG_EHEA) += ehea/
obj-$(CONFIG_EHEA) += ehea/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+8 −0
Original line number Original line Diff line number Diff line
#
# Chelsio T3 driver
#

obj-$(CONFIG_CHELSIO_T3) += cxgb3.o

cxgb3-objs := cxgb3_main.o ael1002.o vsc8211.o t3_hw.o mc5.o \
	      xgmac.o sge.o l2t.o cxgb3_offload.o
+255 −0
Original line number Original line Diff line number Diff line
/*
 * This file is part of the Chelsio T3 Ethernet driver for Linux.
 *
 * Copyright (C) 2003-2006 Chelsio Communications.  All rights reserved.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
 * release for licensing terms and conditions.
 */

/* This file should not be included directly.  Include common.h instead. */

#ifndef __T3_ADAPTER_H__
#define __T3_ADAPTER_H__

#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/cache.h>
#include "t3cdev.h"
#include <asm/semaphore.h>
#include <asm/bitops.h>
#include <asm/io.h>

typedef irqreturn_t(*intr_handler_t) (int, void *);

struct vlan_group;

struct port_info {
	struct vlan_group *vlan_grp;
	const struct port_type_info *port_type;
	u8 port_id;
	u8 rx_csum_offload;
	u8 nqsets;
	u8 first_qset;
	struct cphy phy;
	struct cmac mac;
	struct link_config link_config;
	struct net_device_stats netstats;
	int activity;
};

enum {				/* adapter flags */
	FULL_INIT_DONE = (1 << 0),
	USING_MSI = (1 << 1),
	USING_MSIX = (1 << 2),
};

struct rx_desc;
struct rx_sw_desc;

struct sge_fl {			/* SGE per free-buffer list state */
	unsigned int buf_size;	/* size of each Rx buffer */
	unsigned int credits;	/* # of available Rx buffers */
	unsigned int size;	/* capacity of free list */
	unsigned int cidx;	/* consumer index */
	unsigned int pidx;	/* producer index */
	unsigned int gen;	/* free list generation */
	struct rx_desc *desc;	/* address of HW Rx descriptor ring */
	struct rx_sw_desc *sdesc;	/* address of SW Rx descriptor ring */
	dma_addr_t phys_addr;	/* physical address of HW ring start */
	unsigned int cntxt_id;	/* SGE context id for the free list */
	unsigned long empty;	/* # of times queue ran out of buffers */
};

/*
 * Bundle size for grouping offload RX packets for delivery to the stack.
 * Don't make this too big as we do prefetch on each packet in a bundle.
 */
# define RX_BUNDLE_SIZE 8

struct rsp_desc;

struct sge_rspq {		/* state for an SGE response queue */
	unsigned int credits;	/* # of pending response credits */
	unsigned int size;	/* capacity of response queue */
	unsigned int cidx;	/* consumer index */
	unsigned int gen;	/* current generation bit */
	unsigned int polling;	/* is the queue serviced through NAPI? */
	unsigned int holdoff_tmr;	/* interrupt holdoff timer in 100ns */
	unsigned int next_holdoff;	/* holdoff time for next interrupt */
	struct rsp_desc *desc;	/* address of HW response ring */
	dma_addr_t phys_addr;	/* physical address of the ring */
	unsigned int cntxt_id;	/* SGE context id for the response q */
	spinlock_t lock;	/* guards response processing */
	struct sk_buff *rx_head;	/* offload packet receive queue head */
	struct sk_buff *rx_tail;	/* offload packet receive queue tail */

	unsigned long offload_pkts;
	unsigned long offload_bundles;
	unsigned long eth_pkts;	/* # of ethernet packets */
	unsigned long pure_rsps;	/* # of pure (non-data) responses */
	unsigned long imm_data;	/* responses with immediate data */
	unsigned long rx_drops;	/* # of packets dropped due to no mem */
	unsigned long async_notif; /* # of asynchronous notification events */
	unsigned long empty;	/* # of times queue ran out of credits */
	unsigned long nomem;	/* # of responses deferred due to no mem */
	unsigned long unhandled_irqs;	/* # of spurious intrs */
};

struct tx_desc;
struct tx_sw_desc;

struct sge_txq {		/* state for an SGE Tx queue */
	unsigned long flags;	/* HW DMA fetch status */
	unsigned int in_use;	/* # of in-use Tx descriptors */
	unsigned int size;	/* # of descriptors */
	unsigned int processed;	/* total # of descs HW has processed */
	unsigned int cleaned;	/* total # of descs SW has reclaimed */
	unsigned int stop_thres;	/* SW TX queue suspend threshold */
	unsigned int cidx;	/* consumer index */
	unsigned int pidx;	/* producer index */
	unsigned int gen;	/* current value of generation bit */
	unsigned int unacked;	/* Tx descriptors used since last COMPL */
	struct tx_desc *desc;	/* address of HW Tx descriptor ring */
	struct tx_sw_desc *sdesc;	/* address of SW Tx descriptor ring */
	spinlock_t lock;	/* guards enqueueing of new packets */
	unsigned int token;	/* WR token */
	dma_addr_t phys_addr;	/* physical address of the ring */
	struct sk_buff_head sendq;	/* List of backpressured offload packets */
	struct tasklet_struct qresume_tsk;	/* restarts the queue */
	unsigned int cntxt_id;	/* SGE context id for the Tx q */
	unsigned long stops;	/* # of times q has been stopped */
	unsigned long restarts;	/* # of queue restarts */
};

enum {				/* per port SGE statistics */
	SGE_PSTAT_TSO,		/* # of TSO requests */
	SGE_PSTAT_RX_CSUM_GOOD,	/* # of successful RX csum offloads */
	SGE_PSTAT_TX_CSUM,	/* # of TX checksum offloads */
	SGE_PSTAT_VLANEX,	/* # of VLAN tag extractions */
	SGE_PSTAT_VLANINS,	/* # of VLAN tag insertions */

	SGE_PSTAT_MAX		/* must be last */
};

struct sge_qset {		/* an SGE queue set */
	struct sge_rspq rspq;
	struct sge_fl fl[SGE_RXQ_PER_SET];
	struct sge_txq txq[SGE_TXQ_PER_SET];
	struct net_device *netdev;	/* associated net device */
	unsigned long txq_stopped;	/* which Tx queues are stopped */
	struct timer_list tx_reclaim_timer;	/* reclaims TX buffers */
	unsigned long port_stats[SGE_PSTAT_MAX];
} ____cacheline_aligned;

struct sge {
	struct sge_qset qs[SGE_QSETS];
	spinlock_t reg_lock;	/* guards non-atomic SGE registers (eg context) */
};

struct adapter {
	struct t3cdev tdev;
	struct list_head adapter_list;
	void __iomem *regs;
	struct pci_dev *pdev;
	unsigned long registered_device_map;
	unsigned long open_device_map;
	unsigned long flags;

	const char *name;
	int msg_enable;
	unsigned int mmio_len;

	struct adapter_params params;
	unsigned int slow_intr_mask;
	unsigned long irq_stats[IRQ_NUM_STATS];

	struct {
		unsigned short vec;
		char desc[22];
	} msix_info[SGE_QSETS + 1];

	/* T3 modules */
	struct sge sge;
	struct mc7 pmrx;
	struct mc7 pmtx;
	struct mc7 cm;
	struct mc5 mc5;

	struct net_device *port[MAX_NPORTS];
	unsigned int check_task_cnt;
	struct delayed_work adap_check_task;
	struct work_struct ext_intr_handler_task;

	/*
	 * Dummy netdevices are needed when using multiple receive queues with
	 * NAPI as each netdevice can service only one queue.
	 */
	struct net_device *dummy_netdev[SGE_QSETS - 1];

	struct dentry *debugfs_root;

	struct mutex mdio_lock;
	spinlock_t stats_lock;
	spinlock_t work_lock;
};

static inline u32 t3_read_reg(struct adapter *adapter, u32 reg_addr)
{
	u32 val = readl(adapter->regs + reg_addr);

	CH_DBG(adapter, MMIO, "read register 0x%x value 0x%x\n", reg_addr, val);
	return val;
}

static inline void t3_write_reg(struct adapter *adapter, u32 reg_addr, u32 val)
{
	CH_DBG(adapter, MMIO, "setting register 0x%x to 0x%x\n", reg_addr, val);
	writel(val, adapter->regs + reg_addr);
}

static inline struct port_info *adap2pinfo(struct adapter *adap, int idx)
{
	return netdev_priv(adap->port[idx]);
}

/*
 * We use the spare atalk_ptr to map a net device to its SGE queue set.
 * This is a macro so it can be used as l-value.
 */
#define dev2qset(netdev) ((netdev)->atalk_ptr)

#define OFFLOAD_DEVMAP_BIT 15

#define tdev2adap(d) container_of(d, struct adapter, tdev)

static inline int offload_running(struct adapter *adapter)
{
	return test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map);
}

int t3_offload_tx(struct t3cdev *tdev, struct sk_buff *skb);

void t3_os_ext_intr_handler(struct adapter *adapter);
void t3_os_link_changed(struct adapter *adapter, int port_id, int link_status,
			int speed, int duplex, int fc);

void t3_sge_start(struct adapter *adap);
void t3_sge_stop(struct adapter *adap);
void t3_free_sge_resources(struct adapter *adap);
void t3_sge_err_intr_handler(struct adapter *adapter);
intr_handler_t t3_intr_handler(struct adapter *adap, int polling);
int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev);
void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p);
int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
		      int irq_vec_idx, const struct qset_params *p,
		      int ntxq, struct net_device *netdev);
int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx,
		unsigned char *data);
irqreturn_t t3_sge_intr_msix(int irq, void *cookie);

#endif				/* __T3_ADAPTER_H__ */
+231 −0
Original line number Original line Diff line number Diff line
/*
 * This file is part of the Chelsio T3 Ethernet driver.
 *
 * Copyright (C) 2005-2006 Chelsio Communications.  All rights reserved.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
 * release for licensing terms and conditions.
 */

#include "common.h"
#include "regs.h"

enum {
	AEL100X_TX_DISABLE = 9,
	AEL100X_TX_CONFIG1 = 0xc002,
	AEL1002_PWR_DOWN_HI = 0xc011,
	AEL1002_PWR_DOWN_LO = 0xc012,
	AEL1002_XFI_EQL = 0xc015,
	AEL1002_LB_EN = 0xc017,

	LASI_CTRL = 0x9002,
	LASI_STAT = 0x9005
};

static void ael100x_txon(struct cphy *phy)
{
	int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;

	msleep(100);
	t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
	msleep(30);
}

static int ael1002_power_down(struct cphy *phy, int enable)
{
	int err;

	err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable);
	if (!err)
		err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
					  BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
	return err;
}

static int ael1002_reset(struct cphy *phy, int wait)
{
	int err;

	if ((err = ael1002_power_down(phy, 0)) ||
	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) ||
	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) ||
	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) ||
	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) ||
	    (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN,
				       0, 1 << 5)))
		return err;
	return 0;
}

static int ael1002_intr_noop(struct cphy *phy)
{
	return 0;
}

static int ael100x_get_link_status(struct cphy *phy, int *link_ok,
				   int *speed, int *duplex, int *fc)
{
	if (link_ok) {
		unsigned int status;
		int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status);

		/*
		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
		 * once more to get the current link state.
		 */
		if (!err && !(status & BMSR_LSTATUS))
			err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR,
					&status);
		if (err)
			return err;
		*link_ok = !!(status & BMSR_LSTATUS);
	}
	if (speed)
		*speed = SPEED_10000;
	if (duplex)
		*duplex = DUPLEX_FULL;
	return 0;
}

static struct cphy_ops ael1002_ops = {
	.reset = ael1002_reset,
	.intr_enable = ael1002_intr_noop,
	.intr_disable = ael1002_intr_noop,
	.intr_clear = ael1002_intr_noop,
	.intr_handler = ael1002_intr_noop,
	.get_link_status = ael100x_get_link_status,
	.power_down = ael1002_power_down,
};

void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,
			 int phy_addr, const struct mdio_ops *mdio_ops)
{
	cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops);
	ael100x_txon(phy);
}

static int ael1006_reset(struct cphy *phy, int wait)
{
	return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
}

static int ael1006_intr_enable(struct cphy *phy)
{
	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
}

static int ael1006_intr_disable(struct cphy *phy)
{
	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
}

static int ael1006_intr_clear(struct cphy *phy)
{
	u32 val;

	return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
}

static int ael1006_intr_handler(struct cphy *phy)
{
	unsigned int status;
	int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);

	if (err)
		return err;
	return (status & 1) ? cphy_cause_link_change : 0;
}

static int ael1006_power_down(struct cphy *phy, int enable)
{
	return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
				   BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
}

static struct cphy_ops ael1006_ops = {
	.reset = ael1006_reset,
	.intr_enable = ael1006_intr_enable,
	.intr_disable = ael1006_intr_disable,
	.intr_clear = ael1006_intr_clear,
	.intr_handler = ael1006_intr_handler,
	.get_link_status = ael100x_get_link_status,
	.power_down = ael1006_power_down,
};

void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
			 int phy_addr, const struct mdio_ops *mdio_ops)
{
	cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops);
	ael100x_txon(phy);
}

static struct cphy_ops qt2045_ops = {
	.reset = ael1006_reset,
	.intr_enable = ael1006_intr_enable,
	.intr_disable = ael1006_intr_disable,
	.intr_clear = ael1006_intr_clear,
	.intr_handler = ael1006_intr_handler,
	.get_link_status = ael100x_get_link_status,
	.power_down = ael1006_power_down,
};

void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,
			int phy_addr, const struct mdio_ops *mdio_ops)
{
	unsigned int stat;

	cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops);

	/*
	 * Some cards where the PHY is supposed to be at address 0 actually
	 * have it at 1.
	 */
	if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) &&
	    stat == 0xffff)
		phy->addr = 1;
}

static int xaui_direct_reset(struct cphy *phy, int wait)
{
	return 0;
}

static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
				       int *speed, int *duplex, int *fc)
{
	if (link_ok) {
		unsigned int status;

		status = t3_read_reg(phy->adapter,
				     XGM_REG(A_XGM_SERDES_STAT0, phy->addr));
		*link_ok = !(status & F_LOWSIG0);
	}
	if (speed)
		*speed = SPEED_10000;
	if (duplex)
		*duplex = DUPLEX_FULL;
	return 0;
}

static int xaui_direct_power_down(struct cphy *phy, int enable)
{
	return 0;
}

static struct cphy_ops xaui_direct_ops = {
	.reset = xaui_direct_reset,
	.intr_enable = ael1002_intr_noop,
	.intr_disable = ael1002_intr_noop,
	.intr_clear = ael1002_intr_noop,
	.intr_handler = ael1002_intr_noop,
	.get_link_status = xaui_direct_get_link_status,
	.power_down = xaui_direct_power_down,
};

void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,
			     int phy_addr, const struct mdio_ops *mdio_ops)
{
	cphy_init(phy, adapter, 1, &xaui_direct_ops, mdio_ops);
}
Loading