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 + -
显示快捷键?