Skip to content
Snippets Groups Projects
8139cp.c 53.3 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */
/*
	Copyright 2001-2004 Jeff Garzik <jgarzik@pobox.com>

	Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c]
	Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c]
	Copyright 2001 Manfred Spraul				    [natsemi.c]
	Copyright 1999-2001 by Donald Becker.			    [natsemi.c]
       	Written 1997-2001 by Donald Becker.			    [8139too.c]
	Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>. [acenic.c]

	This software may be used and distributed according to the terms of
	the GNU General Public License (GPL), incorporated herein by reference.
	Drivers based on or derived from this code fall under the GPL and must
	retain the authorship, copyright and license notice.  This file is not
	a complete program and may only be used when the entire operating
	system is licensed under the GPL.

	See the file COPYING in this distribution for more information.

	Contributors:
Linus Torvalds's avatar
Linus Torvalds committed
		Wake-on-LAN support - Felipe Damasio <felipewd@terra.com.br>
		PCI suspend/resume  - Felipe Damasio <felipewd@terra.com.br>
		LinkChg interrupt   - Felipe Damasio <felipewd@terra.com.br>
Linus Torvalds's avatar
Linus Torvalds committed
	TODO:
	* Test Tx checksumming thoroughly

	Low priority TODO:
	* Complete reset on PciErr
	* Consider Rx interrupt mitigation using TimerIntr
	* Investigate using skb->priority with h/w VLAN priority
	* Investigate using High Priority Tx Queue with skb->priority
	* Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error
	* Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error
	* Implement Tx software interrupt mitigation via
	  Tx descriptor bit
	* The real minimum of CP_MIN_MTU is 4 bytes.  However,
	  for this to be supported, one must(?) turn on packet padding.
	* Support external MII transceivers (patch available)

	NOTES:
	* TX checksumming is considered experimental.  It is off by
	  default, use ethtool to turn it on.

 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

Linus Torvalds's avatar
Linus Torvalds committed
#define DRV_NAME		"8139cp"
#define DRV_VERSION		"1.3"
Linus Torvalds's avatar
Linus Torvalds committed
#define DRV_RELDATE		"Mar 22, 2004"


#include <linux/module.h>
#include <linux/moduleparam.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/interrupt.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/pci.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/delay.h>
#include <linux/ethtool.h>
Linus Torvalds's avatar
Linus Torvalds committed
#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/udp.h>
#include <linux/cache.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>

/* These identify the driver base version and may not be removed. */
static char version[] =
DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n";
Linus Torvalds's avatar
Linus Torvalds committed

MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver");
MODULE_VERSION(DRV_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
MODULE_LICENSE("GPL");

static int debug = -1;
module_param(debug, int, 0);
Linus Torvalds's avatar
Linus Torvalds committed
MODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number");

/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
   The RTL chips use a 64 element hash table based on the Ethernet CRC.  */
static int multicast_filter_limit = 32;
module_param(multicast_filter_limit, int, 0);
Linus Torvalds's avatar
Linus Torvalds committed
MODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses");

#define CP_DEF_MSG_ENABLE	(NETIF_MSG_DRV		| \
				 NETIF_MSG_PROBE 	| \
				 NETIF_MSG_LINK)
#define CP_NUM_STATS		14	/* struct cp_dma_stats, plus one */
#define CP_STATS_SIZE		64	/* size in bytes of DMA stats block */
#define CP_REGS_SIZE		(0xff + 1)
#define CP_REGS_VER		1		/* version 1 */
#define CP_RX_RING_SIZE		64
#define CP_TX_RING_SIZE		64
#define CP_RING_BYTES		\
		((sizeof(struct cp_desc) * CP_RX_RING_SIZE) +	\
		 (sizeof(struct cp_desc) * CP_TX_RING_SIZE) +	\
		 CP_STATS_SIZE)
#define NEXT_TX(N)		(((N) + 1) & (CP_TX_RING_SIZE - 1))
#define NEXT_RX(N)		(((N) + 1) & (CP_RX_RING_SIZE - 1))
#define TX_BUFFS_AVAIL(CP)					\
	(((CP)->tx_tail <= (CP)->tx_head) ?			\
	  (CP)->tx_tail + (CP_TX_RING_SIZE - 1) - (CP)->tx_head :	\
	  (CP)->tx_tail - (CP)->tx_head - 1)

#define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer.*/
#define CP_INTERNAL_PHY		32

/* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024, 7==end of packet. */
#define RX_FIFO_THRESH		5	/* Rx buffer level before first PCI xfer.  */
#define RX_DMA_BURST		4	/* Maximum PCI burst, '4' is 256 */
#define TX_DMA_BURST		6	/* Maximum PCI burst, '6' is 1024 */
#define TX_EARLY_THRESH		256	/* Early Tx threshold, in bytes */

/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT		(6*HZ)

/* hardware minimum and maximum for a single frame's data payload */
#define CP_MIN_MTU		60	/* TODO: allow lower, but pad */
#define CP_MAX_MTU		4096

enum {
	/* NIC register offsets */
	MAC0		= 0x00,	/* Ethernet hardware address. */
	MAR0		= 0x08,	/* Multicast filter. */
	StatsAddr	= 0x10,	/* 64-bit start addr of 64-byte DMA stats blk */
	TxRingAddr	= 0x20, /* 64-bit start addr of Tx ring */
	HiTxRingAddr	= 0x28, /* 64-bit start addr of high priority Tx ring */
	Cmd		= 0x37, /* Command register */
	IntrMask	= 0x3C, /* Interrupt mask */
	IntrStatus	= 0x3E, /* Interrupt status */
	TxConfig	= 0x40, /* Tx configuration */
	ChipVersion	= 0x43, /* 8-bit chip version, inside TxConfig */
	RxConfig	= 0x44, /* Rx configuration */
	RxMissed	= 0x4C,	/* 24 bits valid, write clears */
	Cfg9346		= 0x50, /* EEPROM select/control; Cfg reg [un]lock */
	Config1		= 0x52, /* Config1 */
	Config3		= 0x59, /* Config3 */
	Config4		= 0x5A, /* Config4 */
	MultiIntr	= 0x5C, /* Multiple interrupt select */
	BasicModeCtrl	= 0x62,	/* MII BMCR */
	BasicModeStatus	= 0x64, /* MII BMSR */
	NWayAdvert	= 0x66, /* MII ADVERTISE */
	NWayLPAR	= 0x68, /* MII LPA */
	NWayExpansion	= 0x6A, /* MII Expansion */
	Config5		= 0xD8,	/* Config5 */
	TxPoll		= 0xD9,	/* Tell chip to check Tx descriptors for work */
	RxMaxSize	= 0xDA, /* Max size of an Rx packet (8169 only) */
	CpCmd		= 0xE0, /* C+ Command register (C+ mode only) */
	IntrMitigate	= 0xE2,	/* rx/tx interrupt mitigation control */
	RxRingAddr	= 0xE4, /* 64-bit start addr of Rx ring */
	TxThresh	= 0xEC, /* Early Tx threshold */
	OldRxBufAddr	= 0x30, /* DMA address of Rx ring buffer (C mode) */
	OldTSD0		= 0x10, /* DMA address of first Tx desc (C mode) */

	/* Tx and Rx status descriptors */
	DescOwn		= (1 << 31), /* Descriptor is owned by NIC */
	RingEnd		= (1 << 30), /* End of descriptor ring */
	FirstFrag	= (1 << 29), /* First segment of a packet */
	LastFrag	= (1 << 28), /* Final segment of a packet */
	LargeSend	= (1 << 27), /* TCP Large Send Offload (TSO) */
	MSSShift	= 16,	     /* MSS value position */
	MSSMask		= 0xfff,     /* MSS value: 11 bits */
Linus Torvalds's avatar
Linus Torvalds committed
	TxError		= (1 << 23), /* Tx error summary */
	RxError		= (1 << 20), /* Rx error summary */
	IPCS		= (1 << 18), /* Calculate IP checksum */
	UDPCS		= (1 << 17), /* Calculate UDP/IP checksum */
	TCPCS		= (1 << 16), /* Calculate TCP/IP checksum */
	TxVlanTag	= (1 << 17), /* Add VLAN tag */
	RxVlanTagged	= (1 << 16), /* Rx VLAN tag available */
	IPFail		= (1 << 15), /* IP checksum failed */
	UDPFail		= (1 << 14), /* UDP/IP checksum failed */
	TCPFail		= (1 << 13), /* TCP/IP checksum failed */
	NormalTxPoll	= (1 << 6),  /* One or more normal Tx packets to send */
	PID1		= (1 << 17), /* 2 protocol id bits:  0==non-IP, */
	PID0		= (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */
	RxProtoTCP	= 1,
	RxProtoUDP	= 2,
	RxProtoIP	= 3,
	TxFIFOUnder	= (1 << 25), /* Tx FIFO underrun */
	TxOWC		= (1 << 22), /* Tx Out-of-window collision */
	TxLinkFail	= (1 << 21), /* Link failed during Tx of packet */
	TxMaxCol	= (1 << 20), /* Tx aborted due to excessive collisions */
	TxColCntShift	= 16,	     /* Shift, to get 4-bit Tx collision cnt */
	TxColCntMask	= 0x01 | 0x02 | 0x04 | 0x08, /* 4-bit collision count */
	RxErrFrame	= (1 << 27), /* Rx frame alignment error */
	RxMcast		= (1 << 26), /* Rx multicast packet rcv'd */
	RxErrCRC	= (1 << 18), /* Rx CRC error */
	RxErrRunt	= (1 << 19), /* Rx error, packet < 64 bytes */
	RxErrLong	= (1 << 21), /* Rx error, packet > 4096 bytes */
	RxErrFIFO	= (1 << 22), /* Rx error, FIFO overflowed, pkt bad */

	/* StatsAddr register */
	DumpStats	= (1 << 3),  /* Begin stats dump */

	/* RxConfig register */
	RxCfgFIFOShift	= 13,	     /* Shift, to get Rx FIFO thresh value */
	RxCfgDMAShift	= 8,	     /* Shift, to get Rx Max DMA value */
	AcceptErr	= 0x20,	     /* Accept packets with CRC errors */
	AcceptRunt	= 0x10,	     /* Accept runt (<64 bytes) packets */
	AcceptBroadcast	= 0x08,	     /* Accept broadcast packets */
	AcceptMulticast	= 0x04,	     /* Accept multicast packets */
	AcceptMyPhys	= 0x02,	     /* Accept pkts with our MAC as dest */
	AcceptAllPhys	= 0x01,	     /* Accept all pkts w/ physical dest */

	/* IntrMask / IntrStatus registers */
	PciErr		= (1 << 15), /* System error on the PCI bus */
	TimerIntr	= (1 << 14), /* Asserted when TCTR reaches TimerInt value */
	LenChg		= (1 << 13), /* Cable length change */
	SWInt		= (1 << 8),  /* Software-requested interrupt */
	TxEmpty		= (1 << 7),  /* No Tx descriptors available */
	RxFIFOOvr	= (1 << 6),  /* Rx FIFO Overflow */
	LinkChg		= (1 << 5),  /* Packet underrun, or link change */
	RxEmpty		= (1 << 4),  /* No Rx descriptors available */
	TxErr		= (1 << 3),  /* Tx error */
	TxOK		= (1 << 2),  /* Tx packet sent */
	RxErr		= (1 << 1),  /* Rx error */
	RxOK		= (1 << 0),  /* Rx packet received */
	IntrResvd	= (1 << 10), /* reserved, according to RealTek engineers,
					but hardware likes to raise it */

	IntrAll		= PciErr | TimerIntr | LenChg | SWInt | TxEmpty |
			  RxFIFOOvr | LinkChg | RxEmpty | TxErr | TxOK |
			  RxErr | RxOK | IntrResvd,

	/* C mode command register */
	CmdReset	= (1 << 4),  /* Enable to reset; self-clearing */
	RxOn		= (1 << 3),  /* Rx mode enable */
	TxOn		= (1 << 2),  /* Tx mode enable */

	/* C+ mode command register */
	RxVlanOn	= (1 << 6),  /* Rx VLAN de-tagging enable */
	RxChkSum	= (1 << 5),  /* Rx checksum offload enable */
	PCIDAC		= (1 << 4),  /* PCI Dual Address Cycle (64-bit PCI) */
	PCIMulRW	= (1 << 3),  /* Enable PCI read/write multiple */
	CpRxOn		= (1 << 1),  /* Rx mode enable */
	CpTxOn		= (1 << 0),  /* Tx mode enable */

	/* Cfg9436 EEPROM control register */
	Cfg9346_Lock	= 0x00,	     /* Lock ConfigX/MII register access */
	Cfg9346_Unlock	= 0xC0,	     /* Unlock ConfigX/MII register access */

	/* TxConfig register */
	IFG		= (1 << 25) | (1 << 24), /* standard IEEE interframe gap */
	TxDMAShift	= 8,	     /* DMA burst value (0-7) is shift this many bits */

	/* Early Tx Threshold register */
	TxThreshMask	= 0x3f,	     /* Mask bits 5-0 */
	TxThreshMax	= 2048,	     /* Max early Tx threshold */

	/* Config1 register */
	DriverLoaded	= (1 << 5),  /* Software marker, driver is loaded */
	LWACT           = (1 << 4),  /* LWAKE active mode */
	PMEnable	= (1 << 0),  /* Enable various PM features of chip */

	/* Config3 register */
	PARMEnable	= (1 << 6),  /* Enable auto-loading of PHY parms */
	MagicPacket     = (1 << 5),  /* Wake up when receives a Magic Packet */
	LinkUp          = (1 << 4),  /* Wake up when the cable connection is re-established */

	/* Config4 register */
	LWPTN           = (1 << 1),  /* LWAKE Pattern */
	LWPME           = (1 << 4),  /* LANWAKE vs PMEB */

	/* Config5 register */
	BWF             = (1 << 6),  /* Accept Broadcast wakeup frame */
	MWF             = (1 << 5),  /* Accept Multicast wakeup frame */
	UWF             = (1 << 4),  /* Accept Unicast wakeup frame */
	LANWake         = (1 << 1),  /* Enable LANWake signal */
	PMEStatus	= (1 << 0),  /* PME status can be reset by PCI RST# */

	cp_norx_intr_mask = PciErr | LinkChg | TxOK | TxErr | TxEmpty,
	cp_rx_intr_mask = RxOK | RxErr | RxEmpty | RxFIFOOvr,
	cp_intr_mask = cp_rx_intr_mask | cp_norx_intr_mask,
};

static const unsigned int cp_rx_config =
	  (RX_FIFO_THRESH << RxCfgFIFOShift) |
	  (RX_DMA_BURST << RxCfgDMAShift);

struct cp_desc {
	__le32		opts1;
	__le32		opts2;
	__le64		addr;
Linus Torvalds's avatar
Linus Torvalds committed
};

struct cp_dma_stats {
	__le64			tx_ok;
	__le64			rx_ok;
	__le64			tx_err;
	__le32			rx_err;
	__le16			rx_fifo;
	__le16			frame_align;
	__le32			tx_ok_1col;
	__le32			tx_ok_mcol;
	__le64			rx_ok_phys;
	__le64			rx_ok_bcast;
	__le32			rx_ok_mcast;
	__le16			tx_abort;
	__le16			tx_underrun;
} __packed;
Linus Torvalds's avatar
Linus Torvalds committed

struct cp_extra_stats {
	unsigned long		rx_frags;
};

struct cp_private {
	void			__iomem *regs;
	struct net_device	*dev;
	spinlock_t		lock;
	u32			msg_enable;

Linus Torvalds's avatar
Linus Torvalds committed
	struct pci_dev		*pdev;
	u32			rx_config;
	u16			cpcmd;

	struct cp_extra_stats	cp_stats;

	unsigned		rx_head		____cacheline_aligned;
	unsigned		rx_tail;
Linus Torvalds's avatar
Linus Torvalds committed
	struct cp_desc		*rx_ring;
	struct sk_buff		*rx_skb[CP_RX_RING_SIZE];
Linus Torvalds's avatar
Linus Torvalds committed

	unsigned		tx_head		____cacheline_aligned;
	unsigned		tx_tail;
	struct cp_desc		*tx_ring;
	struct sk_buff		*tx_skb[CP_TX_RING_SIZE];

	unsigned		rx_buf_sz;
	unsigned		wol_enabled : 1; /* Is Wake-on-LAN enabled? */
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

	struct mii_if_info	mii_if;
};

#define cpr8(reg)	readb(cp->regs + (reg))
#define cpr16(reg)	readw(cp->regs + (reg))
#define cpr32(reg)	readl(cp->regs + (reg))
#define cpw8(reg,val)	writeb((val), cp->regs + (reg))
#define cpw16(reg,val)	writew((val), cp->regs + (reg))
#define cpw32(reg,val)	writel((val), cp->regs + (reg))
#define cpw8_f(reg,val) do {			\
	writeb((val), cp->regs + (reg));	\
	readb(cp->regs + (reg));		\
	} while (0)
#define cpw16_f(reg,val) do {			\
	writew((val), cp->regs + (reg));	\
	readw(cp->regs + (reg));		\
	} while (0)
#define cpw32_f(reg,val) do {			\
	writel((val), cp->regs + (reg));	\
	readl(cp->regs + (reg));		\
	} while (0)


static void __cp_set_rx_mode (struct net_device *dev);
static void cp_tx (struct cp_private *cp);
static void cp_clean_rings (struct cp_private *cp);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void cp_poll_controller(struct net_device *dev);
#endif
static int cp_get_eeprom_len(struct net_device *dev);
static int cp_get_eeprom(struct net_device *dev,
			 struct ethtool_eeprom *eeprom, u8 *data);
static int cp_set_eeprom(struct net_device *dev,
			 struct ethtool_eeprom *eeprom, u8 *data);
Linus Torvalds's avatar
Linus Torvalds committed

static DEFINE_PCI_DEVICE_TABLE(cp_pci_tbl) = {
	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK,	PCI_DEVICE_ID_REALTEK_8139), },
	{ PCI_DEVICE(PCI_VENDOR_ID_TTTECH,	PCI_DEVICE_ID_TTTECH_MC322), },
Linus Torvalds's avatar
Linus Torvalds committed
	{ },
};
MODULE_DEVICE_TABLE(pci, cp_pci_tbl);

static struct {
	const char str[ETH_GSTRING_LEN];
} ethtool_stats_keys[] = {
	{ "tx_ok" },
	{ "rx_ok" },
	{ "tx_err" },
	{ "rx_err" },
	{ "rx_fifo" },
	{ "frame_align" },
	{ "tx_ok_1col" },
	{ "tx_ok_mcol" },
	{ "rx_ok_phys" },
	{ "rx_ok_bcast" },
	{ "rx_ok_mcast" },
	{ "tx_abort" },
	{ "tx_underrun" },
	{ "rx_frags" },
};


static inline void cp_set_rxbufsize (struct cp_private *cp)
{
	unsigned int mtu = cp->dev->mtu;
Linus Torvalds's avatar
Linus Torvalds committed
	if (mtu > ETH_DATA_LEN)
		/* MTU + ethernet header + FCS + optional VLAN tag */
		cp->rx_buf_sz = mtu + ETH_HLEN + 8;
	else
		cp->rx_buf_sz = PKT_BUF_SZ;
}

static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb,
			      struct cp_desc *desc)
{
	u32 opts2 = le32_to_cpu(desc->opts2);

Linus Torvalds's avatar
Linus Torvalds committed
	skb->protocol = eth_type_trans (skb, cp->dev);

	cp->dev->stats.rx_packets++;
	cp->dev->stats.rx_bytes += skb->len;
Linus Torvalds's avatar
Linus Torvalds committed

	if (opts2 & RxVlanTagged)
		__vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));

	napi_gro_receive(&cp->napi, skb);
Linus Torvalds's avatar
Linus Torvalds committed
}

static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail,
			    u32 status, u32 len)
{
	netif_dbg(cp, rx_err, cp->dev, "rx err, slot %d status 0x%x len %d\n",
		  rx_tail, status, len);
	cp->dev->stats.rx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	if (status & RxErrFrame)
		cp->dev->stats.rx_frame_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	if (status & RxErrCRC)
		cp->dev->stats.rx_crc_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	if ((status & RxErrRunt) || (status & RxErrLong))
		cp->dev->stats.rx_length_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag))
		cp->dev->stats.rx_length_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
	if (status & RxErrFIFO)
		cp->dev->stats.rx_fifo_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
}

static inline unsigned int cp_rx_csum_ok (u32 status)
{
	unsigned int protocol = (status >> 16) & 0x3;
Shan Wei's avatar
Shan Wei committed
	if (((protocol == RxProtoTCP) && !(status & TCPFail)) ||
	    ((protocol == RxProtoUDP) && !(status & UDPFail)))
Linus Torvalds's avatar
Linus Torvalds committed
		return 1;
Shan Wei's avatar
Shan Wei committed
	else
		return 0;
static int cp_rx_poll(struct napi_struct *napi, int budget)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct cp_private *cp = container_of(napi, struct cp_private, napi);
	struct net_device *dev = cp->dev;
	unsigned int rx_tail = cp->rx_tail;
	int rx;
Linus Torvalds's avatar
Linus Torvalds committed

rx_status_loop:
	rx = 0;
	cpw16(IntrStatus, cp_rx_intr_mask);

	while (1) {
		u32 status, len;
		dma_addr_t mapping;
		struct sk_buff *skb, *new_skb;
		struct cp_desc *desc;
		const unsigned buflen = cp->rx_buf_sz;
Linus Torvalds's avatar
Linus Torvalds committed

		skb = cp->rx_skb[rx_tail];
		BUG_ON(!skb);
Linus Torvalds's avatar
Linus Torvalds committed

		desc = &cp->rx_ring[rx_tail];
		status = le32_to_cpu(desc->opts1);
		if (status & DescOwn)
			break;

		len = (status & 0x1fff) - 4;
Francois Romieu's avatar
Francois Romieu committed
		mapping = le64_to_cpu(desc->addr);
Linus Torvalds's avatar
Linus Torvalds committed

		if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) {
			/* we don't support incoming fragmented frames.
			 * instead, we attempt to ensure that the
			 * pre-allocated RX skbs are properly sized such
			 * that RX fragments are never encountered
			 */
			cp_rx_err_acct(cp, rx_tail, status, len);
			dev->stats.rx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
			cp->cp_stats.rx_frags++;
			goto rx_next;
		}

		if (status & (RxError | RxErrFIFO)) {
			cp_rx_err_acct(cp, rx_tail, status, len);
			goto rx_next;
		}

		netif_dbg(cp, rx_status, dev, "rx slot %d status 0x%x len %d\n",
			  rx_tail, status, len);
Linus Torvalds's avatar
Linus Torvalds committed

		new_skb = netdev_alloc_skb_ip_align(dev, buflen);
Linus Torvalds's avatar
Linus Torvalds committed
		if (!new_skb) {
			dev->stats.rx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
			goto rx_next;
		}

		dma_unmap_single(&cp->pdev->dev, mapping,
Linus Torvalds's avatar
Linus Torvalds committed
				 buflen, PCI_DMA_FROMDEVICE);

		/* Handle checksum offloading for incoming packets. */
		if (cp_rx_csum_ok(status))
			skb->ip_summed = CHECKSUM_UNNECESSARY;
		else
			skb_checksum_none_assert(skb);
Linus Torvalds's avatar
Linus Torvalds committed

		skb_put(skb, len);

		mapping = dma_map_single(&cp->pdev->dev, new_skb->data, buflen,
Francois Romieu's avatar
Francois Romieu committed
					 PCI_DMA_FROMDEVICE);
		cp->rx_skb[rx_tail] = new_skb;
Linus Torvalds's avatar
Linus Torvalds committed

		cp_rx_skb(cp, skb, desc);
		rx++;

rx_next:
		cp->rx_ring[rx_tail].opts2 = 0;
		cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping);
		if (rx_tail == (CP_RX_RING_SIZE - 1))
			desc->opts1 = cpu_to_le32(DescOwn | RingEnd |
						  cp->rx_buf_sz);
		else
			desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz);
		rx_tail = NEXT_RX(rx_tail);

Linus Torvalds's avatar
Linus Torvalds committed
			break;
	}

	cp->rx_tail = rx_tail;

	/* if we did not reach work limit, then we're done with
	 * this round of polling
	 */
Linus Torvalds's avatar
Linus Torvalds committed
		if (cpr16(IntrStatus) & cp_rx_intr_mask)
			goto rx_status_loop;

		spin_lock_irqsave(&cp->lock, flags);
		__napi_complete(napi);
		cpw16_f(IntrMask, cp_intr_mask);
		spin_unlock_irqrestore(&cp->lock, flags);
static irqreturn_t cp_interrupt (int irq, void *dev_instance)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct net_device *dev = dev_instance;
	struct cp_private *cp;
	u16 status;

	if (unlikely(dev == NULL))
		return IRQ_NONE;
	cp = netdev_priv(dev);

	status = cpr16(IntrStatus);
	if (!status || (status == 0xFFFF))
		return IRQ_NONE;

	netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n",
		  status, cpr8(Cmd), cpr16(CpCmd));
Linus Torvalds's avatar
Linus Torvalds committed

	cpw16(IntrStatus, status & ~cp_rx_intr_mask);

	spin_lock(&cp->lock);

	/* close possible race's with dev_close */
	if (unlikely(!netif_running(dev))) {
		cpw16(IntrMask, 0);
		spin_unlock(&cp->lock);
		return IRQ_HANDLED;
	}

	if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr))
		if (napi_schedule_prep(&cp->napi)) {
Linus Torvalds's avatar
Linus Torvalds committed
			cpw16_f(IntrMask, cp_norx_intr_mask);
			__napi_schedule(&cp->napi);
Linus Torvalds's avatar
Linus Torvalds committed
		}

	if (status & (TxOK | TxErr | TxEmpty | SWInt))
		cp_tx(cp);
	if (status & LinkChg)
		mii_check_media(&cp->mii_if, netif_msg_link(cp), false);
Linus Torvalds's avatar
Linus Torvalds committed

	spin_unlock(&cp->lock);

	if (status & PciErr) {
		u16 pci_status;

		pci_read_config_word(cp->pdev, PCI_STATUS, &pci_status);
		pci_write_config_word(cp->pdev, PCI_STATUS, pci_status);
		netdev_err(dev, "PCI bus error, status=%04x, PCI status=%04x\n",
			   status, pci_status);
Linus Torvalds's avatar
Linus Torvalds committed

		/* TODO: reset hardware */
	}

	return IRQ_HANDLED;
}

#ifdef CONFIG_NET_POLL_CONTROLLER
/*
 * Polling receive - used by netconsole and other diagnostic tools
 * to allow network i/o with interrupts disabled.
 */
static void cp_poll_controller(struct net_device *dev)
{
	disable_irq(dev->irq);
	enable_irq(dev->irq);
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
static void cp_tx (struct cp_private *cp)
{
	unsigned tx_head = cp->tx_head;
	unsigned tx_tail = cp->tx_tail;

	while (tx_tail != tx_head) {
Francois Romieu's avatar
Francois Romieu committed
		struct cp_desc *txd = cp->tx_ring + tx_tail;
Linus Torvalds's avatar
Linus Torvalds committed
		struct sk_buff *skb;
		u32 status;

		rmb();
Francois Romieu's avatar
Francois Romieu committed
		status = le32_to_cpu(txd->opts1);
Linus Torvalds's avatar
Linus Torvalds committed
		if (status & DescOwn)
			break;

		skb = cp->tx_skb[tx_tail];
		BUG_ON(!skb);
Linus Torvalds's avatar
Linus Torvalds committed

		dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr),
				 le32_to_cpu(txd->opts1) & 0xffff,
				 PCI_DMA_TODEVICE);
Linus Torvalds's avatar
Linus Torvalds committed

		if (status & LastFrag) {
			if (status & (TxError | TxFIFOUnder)) {
				netif_dbg(cp, tx_err, cp->dev,
					  "tx err, status 0x%x\n", status);
				cp->dev->stats.tx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
				if (status & TxOWC)
					cp->dev->stats.tx_window_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
				if (status & TxMaxCol)
					cp->dev->stats.tx_aborted_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
				if (status & TxLinkFail)
					cp->dev->stats.tx_carrier_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
				if (status & TxFIFOUnder)
					cp->dev->stats.tx_fifo_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
			} else {
				cp->dev->stats.collisions +=
Linus Torvalds's avatar
Linus Torvalds committed
					((status >> TxColCntShift) & TxColCntMask);
				cp->dev->stats.tx_packets++;
				cp->dev->stats.tx_bytes += skb->len;
				netif_dbg(cp, tx_done, cp->dev,
					  "tx done, slot %d\n", tx_tail);
Linus Torvalds's avatar
Linus Torvalds committed
			}
			dev_kfree_skb_irq(skb);
		}

		cp->tx_skb[tx_tail] = NULL;
Linus Torvalds's avatar
Linus Torvalds committed

		tx_tail = NEXT_TX(tx_tail);
	}

	cp->tx_tail = tx_tail;

	if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1))
		netif_wake_queue(cp->dev);
}

static inline u32 cp_tx_vlan_tag(struct sk_buff *skb)
{
	return vlan_tx_tag_present(skb) ?
		TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00;
}

static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
					struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct cp_private *cp = netdev_priv(dev);
	unsigned entry;
	u32 eor, flags;
	unsigned long intr_flags;
	__le32 opts2;
	int mss = 0;
Linus Torvalds's avatar
Linus Torvalds committed

	spin_lock_irqsave(&cp->lock, intr_flags);
Linus Torvalds's avatar
Linus Torvalds committed

	/* This is a hard error, log it. */
	if (TX_BUFFS_AVAIL(cp) <= (skb_shinfo(skb)->nr_frags + 1)) {
		netif_stop_queue(dev);
		spin_unlock_irqrestore(&cp->lock, intr_flags);
		netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
Linus Torvalds's avatar
Linus Torvalds committed
	}

	entry = cp->tx_head;
	eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
	mss = skb_shinfo(skb)->gso_size;
	opts2 = cpu_to_le32(cp_tx_vlan_tag(skb));

Linus Torvalds's avatar
Linus Torvalds committed
	if (skb_shinfo(skb)->nr_frags == 0) {
		struct cp_desc *txd = &cp->tx_ring[entry];
		u32 len;
		dma_addr_t mapping;

		len = skb->len;
		mapping = dma_map_single(&cp->pdev->dev, skb->data, len, PCI_DMA_TODEVICE);
		txd->opts2 = opts2;
Linus Torvalds's avatar
Linus Torvalds committed
		txd->addr = cpu_to_le64(mapping);
		wmb();

		flags = eor | len | DescOwn | FirstFrag | LastFrag;

		if (mss)
			flags |= LargeSend | ((mss & MSSMask) << MSSShift);
		else if (skb->ip_summed == CHECKSUM_PARTIAL) {
			const struct iphdr *ip = ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
			if (ip->protocol == IPPROTO_TCP)
				flags |= IPCS | TCPCS;
Linus Torvalds's avatar
Linus Torvalds committed
			else if (ip->protocol == IPPROTO_UDP)
				flags |= IPCS | UDPCS;
Linus Torvalds's avatar
Linus Torvalds committed
			else
				WARN_ON(1);	/* we need a WARN() */
		}

		txd->opts1 = cpu_to_le32(flags);
Linus Torvalds's avatar
Linus Torvalds committed
		wmb();

		cp->tx_skb[entry] = skb;
Linus Torvalds's avatar
Linus Torvalds committed
		entry = NEXT_TX(entry);
	} else {
		struct cp_desc *txd;
		u32 first_len, first_eor;
		dma_addr_t first_mapping;
		int frag, first_entry = entry;
		const struct iphdr *ip = ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed

		/* We must give this initial chunk to the device last.
		 * Otherwise we could race with the device.
		 */
		first_eor = eor;
		first_len = skb_headlen(skb);
		first_mapping = dma_map_single(&cp->pdev->dev, skb->data,
Linus Torvalds's avatar
Linus Torvalds committed
					       first_len, PCI_DMA_TODEVICE);
		cp->tx_skb[entry] = skb;
Linus Torvalds's avatar
Linus Torvalds committed
		entry = NEXT_TX(entry);

		for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
			skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
			u32 len;
			u32 ctrl;
			dma_addr_t mapping;

			len = this_frag->size;
			mapping = dma_map_single(&cp->pdev->dev,
Linus Torvalds's avatar
Linus Torvalds committed
						 ((void *) page_address(this_frag->page) +
						  this_frag->page_offset),
						 len, PCI_DMA_TODEVICE);
			eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;

			ctrl = eor | len | DescOwn;

			if (mss)
				ctrl |= LargeSend |
					((mss & MSSMask) << MSSShift);
			else if (skb->ip_summed == CHECKSUM_PARTIAL) {
Linus Torvalds's avatar
Linus Torvalds committed
				if (ip->protocol == IPPROTO_TCP)
					ctrl |= IPCS | TCPCS;
Linus Torvalds's avatar
Linus Torvalds committed
				else if (ip->protocol == IPPROTO_UDP)
					ctrl |= IPCS | UDPCS;
Linus Torvalds's avatar
Linus Torvalds committed
				else
					BUG();
Linus Torvalds's avatar
Linus Torvalds committed

			if (frag == skb_shinfo(skb)->nr_frags - 1)
				ctrl |= LastFrag;

			txd = &cp->tx_ring[entry];
			txd->opts2 = opts2;
Linus Torvalds's avatar
Linus Torvalds committed
			txd->addr = cpu_to_le64(mapping);
			wmb();

			txd->opts1 = cpu_to_le32(ctrl);
			wmb();

			cp->tx_skb[entry] = skb;
Linus Torvalds's avatar
Linus Torvalds committed
			entry = NEXT_TX(entry);
		}

		txd = &cp->tx_ring[first_entry];
		txd->opts2 = opts2;
Linus Torvalds's avatar
Linus Torvalds committed
		txd->addr = cpu_to_le64(first_mapping);
		wmb();

		if (skb->ip_summed == CHECKSUM_PARTIAL) {
Linus Torvalds's avatar
Linus Torvalds committed
			if (ip->protocol == IPPROTO_TCP)
				txd->opts1 = cpu_to_le32(first_eor | first_len |
							 FirstFrag | DescOwn |
							 IPCS | TCPCS);
			else if (ip->protocol == IPPROTO_UDP)
				txd->opts1 = cpu_to_le32(first_eor | first_len |
							 FirstFrag | DescOwn |
							 IPCS | UDPCS);
			else
				BUG();
		} else
			txd->opts1 = cpu_to_le32(first_eor | first_len |
						 FirstFrag | DescOwn);
		wmb();
	}
	cp->tx_head = entry;
	netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n",
		  entry, skb->len);
Linus Torvalds's avatar
Linus Torvalds committed
	if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1))
		netif_stop_queue(dev);

	spin_unlock_irqrestore(&cp->lock, intr_flags);
Linus Torvalds's avatar
Linus Torvalds committed

	cpw8(TxPoll, NormalTxPoll);

Linus Torvalds's avatar
Linus Torvalds committed
}

/* Set or clear the multicast filter for this adaptor.
   This routine is not state sensitive and need not be SMP locked. */

static void __cp_set_rx_mode (struct net_device *dev)
{
	struct cp_private *cp = netdev_priv(dev);
	u32 mc_filter[2];	/* Multicast hash filter */
	int rx_mode;
Linus Torvalds's avatar
Linus Torvalds committed
	u32 tmp;

	/* Note: do not reorder, GCC is clever about common statements. */
	if (dev->flags & IFF_PROMISC) {
		/* Unconditionally log net taps. */
		rx_mode =
		    AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
		    AcceptAllPhys;
		mc_filter[1] = mc_filter[0] = 0xffffffff;
	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
		   (dev->flags & IFF_ALLMULTI)) {
Linus Torvalds's avatar
Linus Torvalds committed
		/* Too many to filter perfectly -- accept all multicasts. */
		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
		mc_filter[1] = mc_filter[0] = 0xffffffff;
	} else {
		struct netdev_hw_addr *ha;
Linus Torvalds's avatar
Linus Torvalds committed
		rx_mode = AcceptBroadcast | AcceptMyPhys;
		mc_filter[1] = mc_filter[0] = 0;
		netdev_for_each_mc_addr(ha, dev) {
			int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
Linus Torvalds's avatar
Linus Torvalds committed

			mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
			rx_mode |= AcceptMulticast;
		}
	}

	/* We can safely update without stopping the chip. */
	tmp = cp_rx_config | rx_mode;
	if (cp->rx_config != tmp) {
		cpw32_f (RxConfig, tmp);
		cp->rx_config = tmp;
	}
	cpw32_f (MAR0 + 0, mc_filter[0]);
	cpw32_f (MAR0 + 4, mc_filter[1]);
}

static void cp_set_rx_mode (struct net_device *dev)
{
	unsigned long flags;
	struct cp_private *cp = netdev_priv(dev);

	spin_lock_irqsave (&cp->lock, flags);
	__cp_set_rx_mode(dev);
	spin_unlock_irqrestore (&cp->lock, flags);
}

static void __cp_get_stats(struct cp_private *cp)
{
	/* only lower 24 bits valid; write any value to clear */
	cp->dev->stats.rx_missed_errors += (cpr32 (RxMissed) & 0xffffff);
Linus Torvalds's avatar
Linus Torvalds committed
	cpw32 (RxMissed, 0);
}

static struct net_device_stats *cp_get_stats(struct net_device *dev)
{
	struct cp_private *cp = netdev_priv(dev);
	unsigned long flags;

	/* The chip only need report frame silently dropped. */
	spin_lock_irqsave(&cp->lock, flags);
 	if (netif_running(dev) && netif_device_present(dev))
 		__cp_get_stats(cp);
	spin_unlock_irqrestore(&cp->lock, flags);

	return &dev->stats;
Linus Torvalds's avatar
Linus Torvalds committed
}

static void cp_stop_hw (struct cp_private *cp)
{
	cpw16(IntrStatus, ~(cpr16(IntrStatus)));
	cpw16_f(IntrMask, 0);
	cpw8(Cmd, 0);
	cpw16_f(CpCmd, 0);
	cpw16_f(IntrStatus, ~(cpr16(IntrStatus)));

	cp->rx_tail = 0;
	cp->tx_head = cp->tx_tail = 0;
}

static void cp_reset_hw (struct cp_private *cp)
{
	unsigned work = 1000;

	cpw8(Cmd, CmdReset);

	while (work--) {
		if (!(cpr8(Cmd) & CmdReset))
			return;

		schedule_timeout_uninterruptible(10);
	netdev_err(cp->dev, "hardware reset timeout\n");
Linus Torvalds's avatar
Linus Torvalds committed
}

static inline void cp_start_hw (struct cp_private *cp)
{
	cpw16(CpCmd, cp->cpcmd);
	cpw8(Cmd, RxOn | TxOn);
}

static void cp_init_hw (struct cp_private *cp)
{
	struct net_device *dev = cp->dev;
	dma_addr_t ring_dma;

	cp_reset_hw(cp);

	cpw8_f (Cfg9346, Cfg9346_Unlock);

	/* Restore our idea of the MAC address. */
	cpw32_f (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0)));
	cpw32_f (MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4)));
Linus Torvalds's avatar
Linus Torvalds committed

	cp_start_hw(cp);
	cpw8(TxThresh, 0x06); /* XXX convert magic num to a constant */

	__cp_set_rx_mode(dev);
	cpw32_f (TxConfig, IFG | (TX_DMA_BURST << TxDMAShift));

	cpw8(Config1, cpr8(Config1) | DriverLoaded | PMEnable);
	/* Disable Wake-on-LAN. Can be turned on with ETHTOOL_SWOL */
	cpw8(Config3, PARMEnable);
	cp->wol_enabled = 0;

	cpw8(Config5, cpr8(Config5) & PMEStatus);
Linus Torvalds's avatar
Linus Torvalds committed

	cpw32_f(HiTxRingAddr, 0);
	cpw32_f(HiTxRingAddr + 4, 0);

	ring_dma = cp->ring_dma;
	cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
	cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);

	ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
	cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
	cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);