sis190.c
来自「linux 内核源代码」· C语言 代码 · 共 1,862 行 · 第 1/3 页
C
1,862 行
/* sis190.c: Silicon Integrated Systems SiS190 ethernet driver Copyright (c) 2003 K.M. Liu <kmliu@sis.com> Copyright (c) 2003, 2004 Jeff Garzik <jgarzik@pobox.com> Copyright (c) 2003, 2004, 2005 Francois Romieu <romieu@fr.zoreil.com> Based on r8169.c, tg3.c, 8139cp.c, skge.c, epic100.c and SiS 190/191 genuine driver. 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. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/netdevice.h>#include <linux/rtnetlink.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/pci.h>#include <linux/mii.h>#include <linux/delay.h>#include <linux/crc32.h>#include <linux/dma-mapping.h>#include <asm/irq.h>#define net_drv(p, arg...) if (netif_msg_drv(p)) \ printk(arg)#define net_probe(p, arg...) if (netif_msg_probe(p)) \ printk(arg)#define net_link(p, arg...) if (netif_msg_link(p)) \ printk(arg)#define net_intr(p, arg...) if (netif_msg_intr(p)) \ printk(arg)#define net_tx_err(p, arg...) if (netif_msg_tx_err(p)) \ printk(arg)#define PHY_MAX_ADDR 32#define PHY_ID_ANY 0x1f#define MII_REG_ANY 0x1f#define DRV_VERSION "1.2"#define DRV_NAME "sis190"#define SIS190_DRIVER_NAME DRV_NAME " Gigabit Ethernet driver " DRV_VERSION#define PFX DRV_NAME ": "#define sis190_rx_skb netif_rx#define sis190_rx_quota(count, quota) count#define MAC_ADDR_LEN 6#define NUM_TX_DESC 64 /* [8..1024] */#define NUM_RX_DESC 64 /* [8..8192] */#define TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))#define RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))#define RX_BUF_SIZE 1536#define RX_BUF_MASK 0xfff8#define SIS190_REGS_SIZE 0x80#define SIS190_TX_TIMEOUT (6*HZ)#define SIS190_PHY_TIMEOUT (10*HZ)#define SIS190_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ NETIF_MSG_LINK | NETIF_MSG_IFUP | \ NETIF_MSG_IFDOWN)/* Enhanced PHY access register bit definitions */#define EhnMIIread 0x0000#define EhnMIIwrite 0x0020#define EhnMIIdataShift 16#define EhnMIIpmdShift 6 /* 7016 only */#define EhnMIIregShift 11#define EhnMIIreq 0x0010#define EhnMIInotDone 0x0010/* Write/read MMIO register */#define SIS_W8(reg, val) writeb ((val), ioaddr + (reg))#define SIS_W16(reg, val) writew ((val), ioaddr + (reg))#define SIS_W32(reg, val) writel ((val), ioaddr + (reg))#define SIS_R8(reg) readb (ioaddr + (reg))#define SIS_R16(reg) readw (ioaddr + (reg))#define SIS_R32(reg) readl (ioaddr + (reg))#define SIS_PCI_COMMIT() SIS_R32(IntrControl)enum sis190_registers { TxControl = 0x00, TxDescStartAddr = 0x04, rsv0 = 0x08, // reserved TxSts = 0x0c, // unused (Control/Status) RxControl = 0x10, RxDescStartAddr = 0x14, rsv1 = 0x18, // reserved RxSts = 0x1c, // unused IntrStatus = 0x20, IntrMask = 0x24, IntrControl = 0x28, IntrTimer = 0x2c, // unused (Interupt Timer) PMControl = 0x30, // unused (Power Mgmt Control/Status) rsv2 = 0x34, // reserved ROMControl = 0x38, ROMInterface = 0x3c, StationControl = 0x40, GMIIControl = 0x44, GIoCR = 0x48, // unused (GMAC IO Compensation) GIoCtrl = 0x4c, // unused (GMAC IO Control) TxMacControl = 0x50, TxLimit = 0x54, // unused (Tx MAC Timer/TryLimit) RGDelay = 0x58, // unused (RGMII Tx Internal Delay) rsv3 = 0x5c, // reserved RxMacControl = 0x60, RxMacAddr = 0x62, RxHashTable = 0x68, // Undocumented = 0x6c, RxWolCtrl = 0x70, RxWolData = 0x74, // unused (Rx WOL Data Access) RxMPSControl = 0x78, // unused (Rx MPS Control) rsv4 = 0x7c, // reserved};enum sis190_register_content { /* IntrStatus */ SoftInt = 0x40000000, // unused Timeup = 0x20000000, // unused PauseFrame = 0x00080000, // unused MagicPacket = 0x00040000, // unused WakeupFrame = 0x00020000, // unused LinkChange = 0x00010000, RxQEmpty = 0x00000080, RxQInt = 0x00000040, TxQ1Empty = 0x00000020, // unused TxQ1Int = 0x00000010, TxQ0Empty = 0x00000008, // unused TxQ0Int = 0x00000004, RxHalt = 0x00000002, TxHalt = 0x00000001, /* {Rx/Tx}CmdBits */ CmdReset = 0x10, CmdRxEnb = 0x08, // unused CmdTxEnb = 0x01, RxBufEmpty = 0x01, // unused /* Cfg9346Bits */ Cfg9346_Lock = 0x00, // unused Cfg9346_Unlock = 0xc0, // unused /* RxMacControl */ AcceptErr = 0x20, // unused AcceptRunt = 0x10, // unused AcceptBroadcast = 0x0800, AcceptMulticast = 0x0400, AcceptMyPhys = 0x0200, AcceptAllPhys = 0x0100, /* RxConfigBits */ RxCfgFIFOShift = 13, RxCfgDMAShift = 8, // 0x1a in RxControl ? /* TxConfigBits */ TxInterFrameGapShift = 24, TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ LinkStatus = 0x02, // unused FullDup = 0x01, // unused /* TBICSRBit */ TBILinkOK = 0x02000000, // unused};struct TxDesc { __le32 PSize; __le32 status; __le32 addr; __le32 size;};struct RxDesc { __le32 PSize; __le32 status; __le32 addr; __le32 size;};enum _DescStatusBit { /* _Desc.status */ OWNbit = 0x80000000, // RXOWN/TXOWN INTbit = 0x40000000, // RXINT/TXINT CRCbit = 0x00020000, // CRCOFF/CRCEN PADbit = 0x00010000, // PREADD/PADEN /* _Desc.size */ RingEnd = 0x80000000, /* TxDesc.status */ LSEN = 0x08000000, // TSO ? -- FR IPCS = 0x04000000, TCPCS = 0x02000000, UDPCS = 0x01000000, BSTEN = 0x00800000, EXTEN = 0x00400000, DEFEN = 0x00200000, BKFEN = 0x00100000, CRSEN = 0x00080000, COLEN = 0x00040000, THOL3 = 0x30000000, THOL2 = 0x20000000, THOL1 = 0x10000000, THOL0 = 0x00000000, /* RxDesc.status */ IPON = 0x20000000, TCPON = 0x10000000, UDPON = 0x08000000, Wakup = 0x00400000, Magic = 0x00200000, Pause = 0x00100000, DEFbit = 0x00200000, BCAST = 0x000c0000, MCAST = 0x00080000, UCAST = 0x00040000, /* RxDesc.PSize */ TAGON = 0x80000000, RxDescCountMask = 0x7f000000, // multi-desc pkt when > 1 ? -- FR ABORT = 0x00800000, SHORT = 0x00400000, LIMIT = 0x00200000, MIIER = 0x00100000, OVRUN = 0x00080000, NIBON = 0x00040000, COLON = 0x00020000, CRCOK = 0x00010000, RxSizeMask = 0x0000ffff /* * The asic could apparently do vlan, TSO, jumbo (sis191 only) and * provide two (unused with Linux) Tx queues. No publically * available documentation alas. */};enum sis190_eeprom_access_register_bits { EECS = 0x00000001, // unused EECLK = 0x00000002, // unused EEDO = 0x00000008, // unused EEDI = 0x00000004, // unused EEREQ = 0x00000080, EEROP = 0x00000200, EEWOP = 0x00000100 // unused};/* EEPROM Addresses */enum sis190_eeprom_address { EEPROMSignature = 0x00, EEPROMCLK = 0x01, // unused EEPROMInfo = 0x02, EEPROMMACAddr = 0x03};enum sis190_feature { F_HAS_RGMII = 1, F_PHY_88E1111 = 2, F_PHY_BCM5461 = 4};struct sis190_private { void __iomem *mmio_addr; struct pci_dev *pci_dev; struct net_device *dev; spinlock_t lock; u32 rx_buf_sz; u32 cur_rx; u32 cur_tx; u32 dirty_rx; u32 dirty_tx; dma_addr_t rx_dma; dma_addr_t tx_dma; struct RxDesc *RxDescRing; struct TxDesc *TxDescRing; struct sk_buff *Rx_skbuff[NUM_RX_DESC]; struct sk_buff *Tx_skbuff[NUM_TX_DESC]; struct work_struct phy_task; struct timer_list timer; u32 msg_enable; struct mii_if_info mii_if; struct list_head first_phy; u32 features;};struct sis190_phy { struct list_head list; int phy_id; u16 id[2]; u16 status; u8 type;};enum sis190_phy_type { UNKNOWN = 0x00, HOME = 0x01, LAN = 0x02, MIX = 0x03};static struct mii_chip_info { const char *name; u16 id[2]; unsigned int type; u32 feature;} mii_chip_table[] = { { "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN, F_PHY_BCM5461 }, { "Broadcom PHY AC131", { 0x0143, 0xbc70 }, LAN, 0 }, { "Agere PHY ET1101B", { 0x0282, 0xf010 }, LAN, 0 }, { "Marvell PHY 88E1111", { 0x0141, 0x0cc0 }, LAN, F_PHY_88E1111 }, { "Realtek PHY RTL8201", { 0x0000, 0x8200 }, LAN, 0 }, { NULL, }};static const struct { const char *name;} sis_chip_info[] = { { "SiS 190 PCI Fast Ethernet adapter" }, { "SiS 191 PCI Gigabit Ethernet adapter" },};static struct pci_device_id sis190_pci_tbl[] __devinitdata = { { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0190), 0, 0, 0 }, { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0191), 0, 0, 1 }, { 0, },};MODULE_DEVICE_TABLE(pci, sis190_pci_tbl);static int rx_copybreak = 200;static struct { u32 msg_enable;} debug = { -1 };MODULE_DESCRIPTION("SiS sis190 Gigabit Ethernet driver");module_param(rx_copybreak, int, 0);MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");module_param_named(debug, debug.msg_enable, int, 0);MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)");MODULE_AUTHOR("K.M. Liu <kmliu@sis.com>, Ueimor <romieu@fr.zoreil.com>");MODULE_VERSION(DRV_VERSION);MODULE_LICENSE("GPL");static const u32 sis190_intr_mask = RxQEmpty | RxQInt | TxQ1Int | TxQ0Int | RxHalt | TxHalt | LinkChange;/* * Maximum number of multicast addresses to filter (vs. Rx-all-multicast). * The chips use a 64 element hash table based on the Ethernet CRC. */static const int multicast_filter_limit = 32;static void __mdio_cmd(void __iomem *ioaddr, u32 ctl){ unsigned int i; SIS_W32(GMIIControl, ctl); msleep(1); for (i = 0; i < 100; i++) { if (!(SIS_R32(GMIIControl) & EhnMIInotDone)) break; msleep(1); } if (i > 99) printk(KERN_ERR PFX "PHY command failed !\n");}static void mdio_write(void __iomem *ioaddr, int phy_id, int reg, int val){ __mdio_cmd(ioaddr, EhnMIIreq | EhnMIIwrite | (((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift) | (((u32) val) << EhnMIIdataShift));}static int mdio_read(void __iomem *ioaddr, int phy_id, int reg){ __mdio_cmd(ioaddr, EhnMIIreq | EhnMIIread | (((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift)); return (u16) (SIS_R32(GMIIControl) >> EhnMIIdataShift);}static void __mdio_write(struct net_device *dev, int phy_id, int reg, int val){ struct sis190_private *tp = netdev_priv(dev); mdio_write(tp->mmio_addr, phy_id, reg, val);}static int __mdio_read(struct net_device *dev, int phy_id, int reg){ struct sis190_private *tp = netdev_priv(dev); return mdio_read(tp->mmio_addr, phy_id, reg);}static u16 mdio_read_latched(void __iomem *ioaddr, int phy_id, int reg){ mdio_read(ioaddr, phy_id, reg); return mdio_read(ioaddr, phy_id, reg);}static u16 __devinit sis190_read_eeprom(void __iomem *ioaddr, u32 reg){ u16 data = 0xffff; unsigned int i; if (!(SIS_R32(ROMControl) & 0x0002)) return 0; SIS_W32(ROMInterface, EEREQ | EEROP | (reg << 10)); for (i = 0; i < 200; i++) { if (!(SIS_R32(ROMInterface) & EEREQ)) { data = (SIS_R32(ROMInterface) & 0xffff0000) >> 16; break; } msleep(1); } return data;}static void sis190_irq_mask_and_ack(void __iomem *ioaddr){ SIS_W32(IntrMask, 0x00); SIS_W32(IntrStatus, 0xffffffff); SIS_PCI_COMMIT();}static void sis190_asic_down(void __iomem *ioaddr){ /* Stop the chip's Tx and Rx DMA processes. */ SIS_W32(TxControl, 0x1a00); SIS_W32(RxControl, 0x1a00); sis190_irq_mask_and_ack(ioaddr);}static void sis190_mark_as_last_descriptor(struct RxDesc *desc){ desc->size |= cpu_to_le32(RingEnd);}static inline void sis190_give_to_asic(struct RxDesc *desc, u32 rx_buf_sz){ u32 eor = le32_to_cpu(desc->size) & RingEnd; desc->PSize = 0x0; desc->size = cpu_to_le32((rx_buf_sz & RX_BUF_MASK) | eor); wmb(); desc->status = cpu_to_le32(OWNbit | INTbit);}static inline void sis190_map_to_asic(struct RxDesc *desc, dma_addr_t mapping, u32 rx_buf_sz){ desc->addr = cpu_to_le32(mapping); sis190_give_to_asic(desc, rx_buf_sz);}static inline void sis190_make_unusable_by_asic(struct RxDesc *desc){ desc->PSize = 0x0; desc->addr = cpu_to_le32(0xdeadbeef); desc->size &= cpu_to_le32(RingEnd); wmb(); desc->status = 0x0;}static int sis190_alloc_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, struct RxDesc *desc, u32 rx_buf_sz){ struct sk_buff *skb; dma_addr_t mapping; int ret = 0; skb = dev_alloc_skb(rx_buf_sz); if (!skb) goto err_out; *sk_buff = skb; mapping = pci_map_single(pdev, skb->data, rx_buf_sz, PCI_DMA_FROMDEVICE); sis190_map_to_asic(desc, mapping, rx_buf_sz);out: return ret;err_out: ret = -ENOMEM; sis190_make_unusable_by_asic(desc); goto out;}static u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev, u32 start, u32 end){ u32 cur; for (cur = start; cur < end; cur++) { int ret, i = cur % NUM_RX_DESC; if (tp->Rx_skbuff[i]) continue; ret = sis190_alloc_rx_skb(tp->pci_dev, tp->Rx_skbuff + i, tp->RxDescRing + i, tp->rx_buf_sz); if (ret < 0) break; } return cur - start;}static inline int sis190_try_rx_copy(struct sk_buff **sk_buff, int pkt_size, struct RxDesc *desc, int rx_buf_sz){ int ret = -1; if (pkt_size < rx_copybreak) { struct sk_buff *skb; skb = dev_alloc_skb(pkt_size + NET_IP_ALIGN); if (skb) { skb_reserve(skb, NET_IP_ALIGN); skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size); *sk_buff = skb; sis190_give_to_asic(desc, rx_buf_sz); ret = 0; } } return ret;}static inline int sis190_rx_pkt_err(u32 status, struct net_device_stats *stats){#define ErrMask (OVRUN | SHORT | LIMIT | MIIER | NIBON | COLON | ABORT) if ((status & CRCOK) && !(status & ErrMask)) return 0; if (!(status & CRCOK)) stats->rx_crc_errors++; else if (status & OVRUN) stats->rx_over_errors++; else if (status & (SHORT | LIMIT)) stats->rx_length_errors++; else if (status & (MIIER | NIBON | COLON)) stats->rx_frame_errors++; stats->rx_errors++; return -1;}static int sis190_rx_interrupt(struct net_device *dev, struct sis190_private *tp, void __iomem *ioaddr){ struct net_device_stats *stats = &dev->stats; u32 rx_left, cur_rx = tp->cur_rx; u32 delta, count; rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; rx_left = sis190_rx_quota(rx_left, (u32) dev->quota); for (; rx_left > 0; rx_left--, cur_rx++) { unsigned int entry = cur_rx % NUM_RX_DESC; struct RxDesc *desc = tp->RxDescRing + entry; u32 status; if (le32_to_cpu(desc->status) & OWNbit) break; status = le32_to_cpu(desc->PSize); // net_intr(tp, KERN_INFO "%s: Rx PSize = %08x.\n", dev->name, // status); if (sis190_rx_pkt_err(status, stats) < 0) sis190_give_to_asic(desc, tp->rx_buf_sz); else { struct sk_buff *skb = tp->Rx_skbuff[entry]; int pkt_size = (status & RxSizeMask) - 4; void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int) = pci_dma_sync_single_for_device; if (unlikely(pkt_size > tp->rx_buf_sz)) { net_intr(tp, KERN_INFO "%s: (frag) status = %08x.\n", dev->name, status); stats->rx_dropped++; stats->rx_length_errors++; sis190_give_to_asic(desc, tp->rx_buf_sz); continue; } pci_dma_sync_single_for_cpu(tp->pci_dev, le32_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); if (sis190_try_rx_copy(&skb, pkt_size, desc, tp->rx_buf_sz)) { pci_action = pci_unmap_single; tp->Rx_skbuff[entry] = NULL; sis190_make_unusable_by_asic(desc); } pci_action(tp->pci_dev, le32_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?