sc92031.c

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

C
1,620
字号
/*  Silan SC92031 PCI Fast Ethernet Adapter driver * *  Based on vendor drivers: *  Silan Fast Ethernet Netcard Driver: *    MODULE_AUTHOR ("gaoyonghong"); *    MODULE_DESCRIPTION ("SILAN Fast Ethernet driver"); *    MODULE_LICENSE("GPL"); *  8139D Fast Ethernet driver: *    (C) 2002 by gaoyonghong *    MODULE_AUTHOR ("gaoyonghong"); *    MODULE_DESCRIPTION ("Rsltek 8139D PCI Fast Ethernet Adapter driver"); *    MODULE_LICENSE("GPL"); *  Both are almost identical and seem to be based on pci-skeleton.c * *  Rewritten for 2.6 by Cesar Eduardo Barros *//* Note about set_mac_address: I don't know how to change the hardware * matching, so you need to enable IFF_PROMISC when using it. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/crc32.h>#include <asm/irq.h>#define PCI_VENDOR_ID_SILAN		0x1904#define PCI_DEVICE_ID_SILAN_SC92031	0x2031#define PCI_DEVICE_ID_SILAN_8139D	0x8139#define SC92031_NAME "sc92031"#define SC92031_DESCRIPTION "Silan SC92031 PCI Fast Ethernet Adapter driver"#define SC92031_VERSION "2.0c"/* BAR 0 is MMIO, BAR 1 is PIO */#ifndef SC92031_USE_BAR#define SC92031_USE_BAR 0#endif/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */static int multicast_filter_limit = 64;module_param(multicast_filter_limit, int, 0);MODULE_PARM_DESC(multicast_filter_limit,	"Maximum number of filtered multicast addresses");static int media;module_param(media, int, 0);MODULE_PARM_DESC(media, "Media type (0x00 = autodetect,"	" 0x01 = 10M half, 0x02 = 10M full,"	" 0x04 = 100M half, 0x08 = 100M full)");/* Size of the in-memory receive ring. */#define  RX_BUF_LEN_IDX  3 /* 0==8K, 1==16K, 2==32K, 3==64K ,4==128K*/#define  RX_BUF_LEN	(8192 << RX_BUF_LEN_IDX)/* Number of Tx descriptor registers. */#define  NUM_TX_DESC	   4/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/#define  MAX_ETH_FRAME_SIZE	  1536/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */#define  TX_BUF_SIZE       MAX_ETH_FRAME_SIZE#define  TX_BUF_TOT_LEN    (TX_BUF_SIZE * NUM_TX_DESC)/* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024, 7==end of packet. */#define  RX_FIFO_THRESH    7     /* Rx buffer level before first PCI xfer.  *//* Time in jiffies before concluding the transmitter is hung. */#define  TX_TIMEOUT     (4*HZ)#define  SILAN_STATS_NUM    2    /* number of ETHTOOL_GSTATS *//* media options */#define  AUTOSELECT    0x00#define  M10_HALF      0x01#define  M10_FULL      0x02#define  M100_HALF     0x04#define  M100_FULL     0x08 /* Symbolic offsets to registers. */enum  silan_registers {   Config0    = 0x00,         // Config0   Config1    = 0x04,         // Config1   RxBufWPtr  = 0x08,         // Rx buffer writer poiter   IntrStatus = 0x0C,         // Interrupt status   IntrMask   = 0x10,         // Interrupt mask   RxbufAddr  = 0x14,         // Rx buffer start address   RxBufRPtr  = 0x18,         // Rx buffer read pointer   Txstatusall = 0x1C,        // Transmit status of all descriptors   TxStatus0  = 0x20,	      // Transmit status (Four 32bit registers).   TxAddr0    = 0x30,         // Tx descriptors (also four 32bit).   RxConfig   = 0x40,         // Rx configuration   MAC0	      = 0x44,	      // Ethernet hardware address.   MAR0	      = 0x4C,	      // Multicast filter.   RxStatus0  = 0x54,         // Rx status   TxConfig   = 0x5C,         // Tx configuration   PhyCtrl    = 0x60,         // physical control   FlowCtrlConfig = 0x64,     // flow control   Miicmd0    = 0x68,         // Mii command0 register   Miicmd1    = 0x6C,         // Mii command1 register   Miistatus  = 0x70,         // Mii status register   Timercnt   = 0x74,         // Timer counter register   TimerIntr  = 0x78,         // Timer interrupt register   PMConfig   = 0x7C,         // Power Manager configuration   CRC0       = 0x80,         // Power Manager CRC ( Two 32bit regisers)   Wakeup0    = 0x88,         // power Manager wakeup( Eight 64bit regiser)   LSBCRC0    = 0xC8,         // power Manager LSBCRC(Two 32bit regiser)   TestD0     = 0xD0,   TestD4     = 0xD4,   TestD8     = 0xD8,};#define MII_BMCR            0        // Basic mode control register#define MII_BMSR            1        // Basic mode status register#define MII_JAB             16#define MII_OutputStatus    24#define BMCR_FULLDPLX       0x0100    // Full duplex#define BMCR_ANRESTART      0x0200    // Auto negotiation restart#define BMCR_ANENABLE       0x1000    // Enable auto negotiation#define BMCR_SPEED100       0x2000    // Select 100Mbps#define BMSR_LSTATUS        0x0004    // Link status#define PHY_16_JAB_ENB      0x1000#define PHY_16_PORT_ENB     0x1enum IntrStatusBits {   LinkFail       = 0x80000000,   LinkOK         = 0x40000000,   TimeOut        = 0x20000000,   RxOverflow     = 0x0040,   RxOK           = 0x0020,   TxOK           = 0x0001,   IntrBits = LinkFail|LinkOK|TimeOut|RxOverflow|RxOK|TxOK,};enum TxStatusBits {   TxCarrierLost = 0x20000000,   TxAborted     = 0x10000000,   TxOutOfWindow = 0x08000000,   TxNccShift    = 22,   EarlyTxThresShift = 16,   TxStatOK      = 0x8000,   TxUnderrun    = 0x4000,   TxOwn         = 0x2000,};enum RxStatusBits {   RxStatesOK   = 0x80000,   RxBadAlign   = 0x40000,   RxHugeFrame  = 0x20000,   RxSmallFrame = 0x10000,   RxCRCOK      = 0x8000,   RxCrlFrame   = 0x4000,   Rx_Broadcast = 0x2000,   Rx_Multicast = 0x1000,   RxAddrMatch  = 0x0800,   MiiErr       = 0x0400,};enum RxConfigBits {   RxFullDx    = 0x80000000,   RxEnb       = 0x40000000,   RxSmall     = 0x20000000,   RxHuge      = 0x10000000,   RxErr       = 0x08000000,   RxAllphys   = 0x04000000,   RxMulticast = 0x02000000,   RxBroadcast = 0x01000000,   RxLoopBack  = (1 << 23) | (1 << 22),   LowThresholdShift  = 12,   HighThresholdShift = 2,};enum TxConfigBits {   TxFullDx       = 0x80000000,   TxEnb          = 0x40000000,   TxEnbPad       = 0x20000000,   TxEnbHuge      = 0x10000000,   TxEnbFCS       = 0x08000000,   TxNoBackOff    = 0x04000000,   TxEnbPrem      = 0x02000000,   TxCareLostCrs  = 0x1000000,   TxExdCollNum   = 0xf00000,   TxDataRate     = 0x80000,};enum PhyCtrlconfigbits {   PhyCtrlAne         = 0x80000000,   PhyCtrlSpd100      = 0x40000000,   PhyCtrlSpd10       = 0x20000000,   PhyCtrlPhyBaseAddr = 0x1f000000,   PhyCtrlDux         = 0x800000,   PhyCtrlReset       = 0x400000,};enum FlowCtrlConfigBits {   FlowCtrlFullDX = 0x80000000,   FlowCtrlEnb    = 0x40000000,};enum Config0Bits {   Cfg0_Reset  = 0x80000000,   Cfg0_Anaoff = 0x40000000,   Cfg0_LDPS   = 0x20000000,};enum Config1Bits {   Cfg1_EarlyRx = 1 << 31,   Cfg1_EarlyTx = 1 << 30,   //rx buffer size   Cfg1_Rcv8K   = 0x0,   Cfg1_Rcv16K  = 0x1,   Cfg1_Rcv32K  = 0x3,   Cfg1_Rcv64K  = 0x7,   Cfg1_Rcv128K = 0xf,};enum MiiCmd0Bits {   Mii_Divider = 0x20000000,   Mii_WRITE   = 0x400000,   Mii_READ    = 0x200000,   Mii_SCAN    = 0x100000,   Mii_Tamod   = 0x80000,   Mii_Drvmod  = 0x40000,   Mii_mdc     = 0x20000,   Mii_mdoen   = 0x10000,   Mii_mdo     = 0x8000,   Mii_mdi     = 0x4000,};enum MiiStatusBits {    Mii_StatusBusy = 0x80000000,};enum PMConfigBits {   PM_Enable  = 1 << 31,   PM_LongWF  = 1 << 30,   PM_Magic   = 1 << 29,   PM_LANWake = 1 << 28,   PM_LWPTN   = (1 << 27 | 1<< 26),   PM_LinkUp  = 1 << 25,   PM_WakeUp  = 1 << 24,};/* Locking rules: * priv->lock protects most of the fields of priv and most of the * hardware registers. It does not have to protect against softirqs * between sc92031_disable_interrupts and sc92031_enable_interrupts; * it also does not need to be used in ->open and ->stop while the * device interrupts are off. * Not having to protect against softirqs is very useful due to heavy * use of mdelay() at _sc92031_reset. * Functions prefixed with _sc92031_ must be called with the lock held; * functions prefixed with sc92031_ must be called without the lock held. * Use mmiowb() before unlocking if the hardware was written to. *//* Locking rules for the interrupt: * - the interrupt and the tasklet never run at the same time * - neither run between sc92031_disable_interrupts and *   sc92031_enable_interrupt */struct sc92031_priv {	spinlock_t		lock;	/* iomap.h cookie */	void __iomem		*port_base;	/* pci device structure */	struct pci_dev		*pdev;	/* tasklet */	struct tasklet_struct	tasklet;	/* CPU address of rx ring */	void			*rx_ring;	/* PCI address of rx ring */	dma_addr_t		rx_ring_dma_addr;	/* PCI address of rx ring read pointer */	dma_addr_t		rx_ring_tail;	/* tx ring write index */	unsigned		tx_head;	/* tx ring read index */	unsigned		tx_tail;	/* CPU address of tx bounce buffer */	void			*tx_bufs;	/* PCI address of tx bounce buffer */	dma_addr_t		tx_bufs_dma_addr;	/* copies of some hardware registers */	u32			intr_status;	atomic_t		intr_mask;	u32			rx_config;	u32			tx_config;	u32			pm_config;	/* copy of some flags from dev->flags */	unsigned int		mc_flags;	/* for ETHTOOL_GSTATS */	u64			tx_timeouts;	u64			rx_loss;	/* for dev->get_stats */	long			rx_value;	struct net_device_stats	stats;};/* I don't know which registers can be safely read; however, I can guess * MAC0 is one of them. */static inline void _sc92031_dummy_read(void __iomem *port_base){	ioread32(port_base + MAC0);}static u32 _sc92031_mii_wait(void __iomem *port_base){	u32 mii_status;	do {		udelay(10);		mii_status = ioread32(port_base + Miistatus);	} while (mii_status & Mii_StatusBusy);	return mii_status;}static u32 _sc92031_mii_cmd(void __iomem *port_base, u32 cmd0, u32 cmd1){	iowrite32(Mii_Divider, port_base + Miicmd0);	_sc92031_mii_wait(port_base);	iowrite32(cmd1, port_base + Miicmd1);	iowrite32(Mii_Divider | cmd0, port_base + Miicmd0);	return _sc92031_mii_wait(port_base);}static void _sc92031_mii_scan(void __iomem *port_base){	_sc92031_mii_cmd(port_base, Mii_SCAN, 0x1 << 6);}static u16 _sc92031_mii_read(void __iomem *port_base, unsigned reg){	return _sc92031_mii_cmd(port_base, Mii_READ, reg << 6) >> 13;}static void _sc92031_mii_write(void __iomem *port_base, unsigned reg, u16 val){	_sc92031_mii_cmd(port_base, Mii_WRITE, (reg << 6) | ((u32)val << 11));}static void sc92031_disable_interrupts(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	/* tell the tasklet/interrupt not to enable interrupts */	atomic_set(&priv->intr_mask, 0);	wmb();	/* stop interrupts */	iowrite32(0, port_base + IntrMask);	_sc92031_dummy_read(port_base);	mmiowb();	/* wait for any concurrent interrupt/tasklet to finish */	synchronize_irq(dev->irq);	tasklet_disable(&priv->tasklet);}static void sc92031_enable_interrupts(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	tasklet_enable(&priv->tasklet);	atomic_set(&priv->intr_mask, IntrBits);	wmb();	iowrite32(IntrBits, port_base + IntrMask);	mmiowb();}static void _sc92031_disable_tx_rx(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	priv->rx_config &= ~RxEnb;	priv->tx_config &= ~TxEnb;	iowrite32(priv->rx_config, port_base + RxConfig);	iowrite32(priv->tx_config, port_base + TxConfig);}static void _sc92031_enable_tx_rx(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	priv->rx_config |= RxEnb;	priv->tx_config |= TxEnb;	iowrite32(priv->rx_config, port_base + RxConfig);	iowrite32(priv->tx_config, port_base + TxConfig);}static void _sc92031_tx_clear(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	while (priv->tx_head - priv->tx_tail > 0) {		priv->tx_tail++;		priv->stats.tx_dropped++;	}	priv->tx_head = priv->tx_tail = 0;}static void _sc92031_set_mar(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	u32 mar0 = 0, mar1 = 0;	if ((dev->flags & IFF_PROMISC)			|| dev->mc_count > multicast_filter_limit			|| (dev->flags & IFF_ALLMULTI))		mar0 = mar1 = 0xffffffff;	else if (dev->flags & IFF_MULTICAST) {		struct dev_mc_list *mc_list;		for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {			u32 crc;			unsigned bit = 0;			crc = ~ether_crc(ETH_ALEN, mc_list->dmi_addr);			crc >>= 24;			if (crc & 0x01)	bit |= 0x02;			if (crc & 0x02)	bit |= 0x01;			if (crc & 0x10)	bit |= 0x20;			if (crc & 0x20)	bit |= 0x10;			if (crc & 0x40)	bit |= 0x08;			if (crc & 0x80)	bit |= 0x04;			if (bit > 31)				mar0 |= 0x1 << (bit - 32);			else				mar1 |= 0x1 << bit;		}	}	iowrite32(mar0, port_base + MAR0);	iowrite32(mar1, port_base + MAR0 + 4);}static void _sc92031_set_rx_config(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	unsigned int old_mc_flags;	u32 rx_config_bits = 0;	old_mc_flags = priv->mc_flags;	if (dev->flags & IFF_PROMISC)		rx_config_bits |= RxSmall | RxHuge | RxErr | RxBroadcast				| RxMulticast | RxAllphys;	if (dev->flags & (IFF_ALLMULTI | IFF_MULTICAST))		rx_config_bits |= RxMulticast;	if (dev->flags & IFF_BROADCAST)		rx_config_bits |= RxBroadcast;	priv->rx_config &= ~(RxSmall | RxHuge | RxErr | RxBroadcast			| RxMulticast | RxAllphys);	priv->rx_config |= rx_config_bits;	priv->mc_flags = dev->flags & (IFF_PROMISC | IFF_ALLMULTI			| IFF_MULTICAST | IFF_BROADCAST);	if (netif_carrier_ok(dev) && priv->mc_flags != old_mc_flags)		iowrite32(priv->rx_config, port_base + RxConfig);}static bool _sc92031_check_media(struct net_device *dev){	struct sc92031_priv *priv = netdev_priv(dev);	void __iomem *port_base = priv->port_base;	u16 bmsr;	bmsr = _sc92031_mii_read(port_base, MII_BMSR);	rmb();	if (bmsr & BMSR_LSTATUS) {		bool speed_100, duplex_full;		u32 flow_ctrl_config = 0;		u16 output_status = _sc92031_mii_read(port_base,				MII_OutputStatus);		_sc92031_mii_scan(port_base);		speed_100 = output_status & 0x2;		duplex_full = output_status & 0x4;		/* Initial Tx/Rx configuration */		priv->rx_config = (0x40 << LowThresholdShift) | (0x1c0 << HighThresholdShift);		priv->tx_config = 0x48800000;		/* NOTE: vendor driver had dead code here to enable tx padding */		if (!speed_100)			priv->tx_config |= 0x80000;		// configure rx mode		_sc92031_set_rx_config(dev);		if (duplex_full) {			priv->rx_config |= RxFullDx;			priv->tx_config |= TxFullDx;			flow_ctrl_config = FlowCtrlFullDX | FlowCtrlEnb;		} else {			priv->rx_config &= ~RxFullDx;			priv->tx_config &= ~TxFullDx;		}		_sc92031_set_mar(dev);		_sc92031_set_rx_config(dev);		_sc92031_enable_tx_rx(dev);		iowrite32(flow_ctrl_config, port_base + FlowCtrlConfig);

⌨️ 快捷键说明

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