cassini.c

来自「linux 内核源代码」· C语言 代码 · 共 2,335 行 · 第 1/5 页

C
2,335
字号
/* cassini.c: Sun Microsystems Cassini(+) ethernet driver. * * Copyright (C) 2004 Sun Microsystems Inc. * Copyright (C) 2003 Adrian Sun (asun@darksunrising.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * This driver uses the sungem driver (c) David Miller * (davem@redhat.com) as its basis. * * The cassini chip has a number of features that distinguish it from * the gem chip: *  4 transmit descriptor rings that are used for either QoS (VLAN) or *      load balancing (non-VLAN mode) *  batching of multiple packets *  multiple CPU dispatching *  page-based RX descriptor engine with separate completion rings *  Gigabit support (GMII and PCS interface) *  MIF link up/down detection works * * RX is handled by page sized buffers that are attached as fragments to * the skb. here's what's done: *  -- driver allocates pages at a time and keeps reference counts *     on them. *  -- the upper protocol layers assume that the header is in the skb *     itself. as a result, cassini will copy a small amount (64 bytes) *     to make them happy. *  -- driver appends the rest of the data pages as frags to skbuffs *     and increments the reference count *  -- on page reclamation, the driver swaps the page with a spare page. *     if that page is still in use, it frees its reference to that page, *     and allocates a new page for use. otherwise, it just recycles the *     the page. * * NOTE: cassini can parse the header. however, it's not worth it *       as long as the network stack requires a header copy. * * TX has 4 queues. currently these queues are used in a round-robin * fashion for load balancing. They can also be used for QoS. for that * to work, however, QoS information needs to be exposed down to the driver * level so that subqueues get targetted to particular transmit rings. * alternatively, the queues can be configured via use of the all-purpose * ioctl. * * RX DATA: the rx completion ring has all the info, but the rx desc * ring has all of the data. RX can conceivably come in under multiple * interrupts, but the INT# assignment needs to be set up properly by * the BIOS and conveyed to the driver. PCI BIOSes don't know how to do * that. also, the two descriptor rings are designed to distinguish between * encrypted and non-encrypted packets, but we use them for buffering * instead. * * by default, the selective clear mask is set up to process rx packets. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/compiler.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/mm.h>#include <linux/highmem.h>#include <linux/list.h>#include <linux/dma-mapping.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/ethtool.h>#include <linux/crc32.h>#include <linux/random.h>#include <linux/mii.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/mutex.h>#include <net/checksum.h>#include <asm/atomic.h>#include <asm/system.h>#include <asm/io.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#define cas_page_map(x)      kmap_atomic((x), KM_SKB_DATA_SOFTIRQ)#define cas_page_unmap(x)    kunmap_atomic((x), KM_SKB_DATA_SOFTIRQ)#define CAS_NCPUS            num_online_cpus()#if defined(CONFIG_CASSINI_NAPI) && defined(HAVE_NETDEV_POLL)#define USE_NAPI#define cas_skb_release(x)  netif_receive_skb(x)#else#define cas_skb_release(x)  netif_rx(x)#endif/* select which firmware to use */#define USE_HP_WORKAROUND#define HP_WORKAROUND_DEFAULT /* select which firmware to use as default */#define CAS_HP_ALT_FIRMWARE   cas_prog_null /* alternate firmware */#include "cassini.h"#define USE_TX_COMPWB      /* use completion writeback registers */#define USE_CSMA_CD_PROTO  /* standard CSMA/CD */#define USE_RX_BLANK       /* hw interrupt mitigation */#undef USE_ENTROPY_DEV     /* don't test for entropy device *//* NOTE: these aren't useable unless PCI interrupts can be assigned. * also, we need to make cp->lock finer-grained. */#undef  USE_PCI_INTB#undef  USE_PCI_INTC#undef  USE_PCI_INTD#undef  USE_QOS#undef  USE_VPD_DEBUG       /* debug vpd information if defined *//* rx processing options */#define USE_PAGE_ORDER      /* specify to allocate large rx pages */#define RX_DONT_BATCH  0    /* if 1, don't batch flows */#define RX_COPY_ALWAYS 0    /* if 0, use frags */#define RX_COPY_MIN    64   /* copy a little to make upper layers happy */#undef  RX_COUNT_BUFFERS    /* define to calculate RX buffer stats */#define DRV_MODULE_NAME		"cassini"#define PFX DRV_MODULE_NAME	": "#define DRV_MODULE_VERSION	"1.5"#define DRV_MODULE_RELDATE	"4 Jan 2008"#define CAS_DEF_MSG_ENABLE	  \	(NETIF_MSG_DRV		| \	 NETIF_MSG_PROBE	| \	 NETIF_MSG_LINK		| \	 NETIF_MSG_TIMER	| \	 NETIF_MSG_IFDOWN	| \	 NETIF_MSG_IFUP		| \	 NETIF_MSG_RX_ERR	| \	 NETIF_MSG_TX_ERR)/* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */#define CAS_TX_TIMEOUT			(HZ)#define CAS_LINK_TIMEOUT                (22*HZ/10)#define CAS_LINK_FAST_TIMEOUT           (1)/* timeout values for state changing. these specify the number * of 10us delays to be used before giving up. */#define STOP_TRIES_PHY 1000#define STOP_TRIES     5000/* specify a minimum frame size to deal with some fifo issues * max mtu == 2 * page size - ethernet header - 64 - swivel = *            2 * page_size - 0x50 */#define CAS_MIN_FRAME			97#define CAS_1000MB_MIN_FRAME            255#define CAS_MIN_MTU                     60#define CAS_MAX_MTU                     min(((cp->page_size << 1) - 0x50), 9000)#if 1/* * Eliminate these and use separate atomic counters for each, to * avoid a race condition. */#else#define CAS_RESET_MTU                   1#define CAS_RESET_ALL                   2#define CAS_RESET_SPARE                 3#endifstatic char version[] __devinitdata =	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";static int cassini_debug = -1;	/* -1 == use CAS_DEF_MSG_ENABLE as value */static int link_mode;MODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)");MODULE_DESCRIPTION("Sun Cassini(+) ethernet driver");MODULE_LICENSE("GPL");module_param(cassini_debug, int, 0);MODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value");module_param(link_mode, int, 0);MODULE_PARM_DESC(link_mode, "default link mode");/* * Work around for a PCS bug in which the link goes down due to the chip * being confused and never showing a link status of "up." */#define DEFAULT_LINKDOWN_TIMEOUT 5/* * Value in seconds, for user input. */static int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT;module_param(linkdown_timeout, int, 0);MODULE_PARM_DESC(linkdown_timeout,"min reset interval in sec. for PCS linkdown issue; disabled if not positive");/* * value in 'ticks' (units used by jiffies). Set when we init the * module because 'HZ' in actually a function call on some flavors of * Linux.  This will default to DEFAULT_LINKDOWN_TIMEOUT * HZ. */static int link_transition_timeout;static u16 link_modes[] __devinitdata = {	BMCR_ANENABLE,			 /* 0 : autoneg */	0,				 /* 1 : 10bt half duplex */	BMCR_SPEED100,			 /* 2 : 100bt half duplex */	BMCR_FULLDPLX,			 /* 3 : 10bt full duplex */	BMCR_SPEED100|BMCR_FULLDPLX,	 /* 4 : 100bt full duplex */	CAS_BMCR_SPEED1000|BMCR_FULLDPLX /* 5 : 1000bt full duplex */};static struct pci_device_id cas_pci_tbl[] __devinitdata = {	{ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_CASSINI,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SATURN,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ 0, }};MODULE_DEVICE_TABLE(pci, cas_pci_tbl);static void cas_set_link_modes(struct cas *cp);static inline void cas_lock_tx(struct cas *cp){	int i;	for (i = 0; i < N_TX_RINGS; i++)		spin_lock(&cp->tx_lock[i]);}static inline void cas_lock_all(struct cas *cp){	spin_lock_irq(&cp->lock);	cas_lock_tx(cp);}/* WTZ: QA was finding deadlock problems with the previous * versions after long test runs with multiple cards per machine. * See if replacing cas_lock_all with safer versions helps. The * symptoms QA is reporting match those we'd expect if interrupts * aren't being properly restored, and we fixed a previous deadlock * with similar symptoms by using save/restore versions in other * places. */#define cas_lock_all_save(cp, flags) \do { \	struct cas *xxxcp = (cp); \	spin_lock_irqsave(&xxxcp->lock, flags); \	cas_lock_tx(xxxcp); \} while (0)static inline void cas_unlock_tx(struct cas *cp){	int i;	for (i = N_TX_RINGS; i > 0; i--)		spin_unlock(&cp->tx_lock[i - 1]);}static inline void cas_unlock_all(struct cas *cp){	cas_unlock_tx(cp);	spin_unlock_irq(&cp->lock);}#define cas_unlock_all_restore(cp, flags) \do { \	struct cas *xxxcp = (cp); \	cas_unlock_tx(xxxcp); \	spin_unlock_irqrestore(&xxxcp->lock, flags); \} while (0)static void cas_disable_irq(struct cas *cp, const int ring){	/* Make sure we won't get any more interrupts */	if (ring == 0) {		writel(0xFFFFFFFF, cp->regs + REG_INTR_MASK);		return;	}	/* disable completion interrupts and selectively mask */	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {		switch (ring) {#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)#ifdef USE_PCI_INTB		case 1:#endif#ifdef USE_PCI_INTC		case 2:#endif#ifdef USE_PCI_INTD		case 3:#endif			writel(INTRN_MASK_CLEAR_ALL | INTRN_MASK_RX_EN,			       cp->regs + REG_PLUS_INTRN_MASK(ring));			break;#endif		default:			writel(INTRN_MASK_CLEAR_ALL, cp->regs +			       REG_PLUS_INTRN_MASK(ring));			break;		}	}}static inline void cas_mask_intr(struct cas *cp){	int i;	for (i = 0; i < N_RX_COMP_RINGS; i++)		cas_disable_irq(cp, i);}static void cas_enable_irq(struct cas *cp, const int ring){	if (ring == 0) { /* all but TX_DONE */		writel(INTR_TX_DONE, cp->regs + REG_INTR_MASK);		return;	}	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {		switch (ring) {#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)#ifdef USE_PCI_INTB		case 1:#endif#ifdef USE_PCI_INTC		case 2:#endif#ifdef USE_PCI_INTD		case 3:#endif			writel(INTRN_MASK_RX_EN, cp->regs +			       REG_PLUS_INTRN_MASK(ring));			break;#endif		default:			break;		}	}}static inline void cas_unmask_intr(struct cas *cp){	int i;	for (i = 0; i < N_RX_COMP_RINGS; i++)		cas_enable_irq(cp, i);}static inline void cas_entropy_gather(struct cas *cp){#ifdef USE_ENTROPY_DEV	if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0)		return;	batch_entropy_store(readl(cp->regs + REG_ENTROPY_IV),			    readl(cp->regs + REG_ENTROPY_IV),			    sizeof(uint64_t)*8);#endif}static inline void cas_entropy_reset(struct cas *cp){#ifdef USE_ENTROPY_DEV	if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0)		return;	writel(BIM_LOCAL_DEV_PAD | BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_EXT,	       cp->regs + REG_BIM_LOCAL_DEV_EN);	writeb(ENTROPY_RESET_STC_MODE, cp->regs + REG_ENTROPY_RESET);	writeb(0x55, cp->regs + REG_ENTROPY_RAND_REG);	/* if we read back 0x0, we don't have an entropy device */	if (readb(cp->regs + REG_ENTROPY_RAND_REG) == 0)		cp->cas_flags &= ~CAS_FLAG_ENTROPY_DEV;#endif}/* access to the phy. the following assumes that we've initialized the MIF to * be in frame rather than bit-bang mode */static u16 cas_phy_read(struct cas *cp, int reg){	u32 cmd;	int limit = STOP_TRIES_PHY;	cmd = MIF_FRAME_ST | MIF_FRAME_OP_READ;	cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr);	cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg);	cmd |= MIF_FRAME_TURN_AROUND_MSB;	writel(cmd, cp->regs + REG_MIF_FRAME);	/* poll for completion */	while (limit-- > 0) {		udelay(10);		cmd = readl(cp->regs + REG_MIF_FRAME);		if (cmd & MIF_FRAME_TURN_AROUND_LSB)			return (cmd & MIF_FRAME_DATA_MASK);	}	return 0xFFFF; /* -1 */}static int cas_phy_write(struct cas *cp, int reg, u16 val){	int limit = STOP_TRIES_PHY;	u32 cmd;	cmd = MIF_FRAME_ST | MIF_FRAME_OP_WRITE;	cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr);	cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg);	cmd |= MIF_FRAME_TURN_AROUND_MSB;	cmd |= val & MIF_FRAME_DATA_MASK;	writel(cmd, cp->regs + REG_MIF_FRAME);	/* poll for completion */	while (limit-- > 0) {		udelay(10);		cmd = readl(cp->regs + REG_MIF_FRAME);		if (cmd & MIF_FRAME_TURN_AROUND_LSB)			return 0;	}	return -1;}static void cas_phy_powerup(struct cas *cp){	u16 ctl = cas_phy_read(cp, MII_BMCR);	if ((ctl & BMCR_PDOWN) == 0)		return;	ctl &= ~BMCR_PDOWN;	cas_phy_write(cp, MII_BMCR, ctl);}static void cas_phy_powerdown(struct cas *cp){	u16 ctl = cas_phy_read(cp, MII_BMCR);	if (ctl & BMCR_PDOWN)		return;	ctl |= BMCR_PDOWN;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?