📄 sungem.c
字号:
/* $Id: sungem.c,v 1.44.2.5 2002/02/01 21:45:52 davem Exp $ * sungem.c: Sun GEM ethernet driver. * * Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) * * Support for Apple GMAC and assorted PHYs by * Benjamin Herrenscmidt (benh@kernel.crashing.org) * * TODO: * - Get rid of all those nasty mdelay's and replace them * with schedule_timeout. * - Implement WOL * - Currently, forced Gb mode is only supported on bcm54xx * PHY for which I use the SPD2 bit of the control register. * On m1011 PHY, I can't force as I don't have the specs, but * I can at least detect gigabit with autoneg. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#include <asm/irq.h>#ifdef __sparc__#include <asm/idprom.h>#include <asm/openprom.h>#include <asm/oplib.h>#include <asm/pbm.h>#endif#ifdef CONFIG_ALL_PPC#include <asm/pci-bridge.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/pmac_feature.h>#endif#include "sungem.h"#define DEFAULT_MSG (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK)#define DRV_NAME "sungem"#define DRV_VERSION "0.96"#define DRV_RELDATE "11/17/01"#define DRV_AUTHOR "David S. Miller (davem@redhat.com)"static char version[] __devinitdata = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");MODULE_LICENSE("GPL");MODULE_PARM(gem_debug, "i");MODULE_PARM_DESC(gem_debug, "bitmapped message enable number");MODULE_PARM(link_mode, "i");MODULE_PARM_DESC(link_mode, "default link mode");int gem_debug = -1;static int link_mode;static u16 link_modes[] __devinitdata = { BMCR_ANENABLE, /* 0 : autoneg */ 0, /* 1 : 10bt half duplex */ BMCR_SPEED100, /* 2 : 100bt half duplex */ BMCR_SPD2, /* bcm54xx only */ /* 3 : 1000bt half duplex */ BMCR_FULLDPLX, /* 4 : 10bt full duplex */ BMCR_SPEED100|BMCR_FULLDPLX, /* 5 : 100bt full duplex */ BMCR_SPD2|BMCR_FULLDPLX /* 6 : 1000bt full duplex */};#define GEM_MODULE_NAME "gem"#define PFX GEM_MODULE_NAME ": "static struct pci_device_id gem_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_GEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* These models only differ from the original GEM in * that their tx/rx fifos are of a different size and * they only support 10/100 speeds. -DaveM * * Apple's GMAC does support gigabit on machines with * the BCM54xx PHYs. -BenH */ { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_RIO_GEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, {0, }};MODULE_DEVICE_TABLE(pci, gem_pci_tbl);static u16 __phy_read(struct gem *gp, int reg, int phy_addr){ u32 cmd; int limit = 10000; cmd = (1 << 30); cmd |= (2 << 28); cmd |= (phy_addr << 23) & MIF_FRAME_PHYAD; cmd |= (reg << 18) & MIF_FRAME_REGAD; cmd |= (MIF_FRAME_TAMSB); writel(cmd, gp->regs + MIF_FRAME); while (limit--) { cmd = readl(gp->regs + MIF_FRAME); if (cmd & MIF_FRAME_TALSB) break; udelay(10); } if (!limit) cmd = 0xffff; return cmd & MIF_FRAME_DATA;}static inline u16 phy_read(struct gem *gp, int reg){ return __phy_read(gp, reg, gp->mii_phy_addr);}static void __phy_write(struct gem *gp, int reg, u16 val, int phy_addr){ u32 cmd; int limit = 10000; cmd = (1 << 30); cmd |= (1 << 28); cmd |= (phy_addr << 23) & MIF_FRAME_PHYAD; cmd |= (reg << 18) & MIF_FRAME_REGAD; cmd |= (MIF_FRAME_TAMSB); cmd |= (val & MIF_FRAME_DATA); writel(cmd, gp->regs + MIF_FRAME); while (limit--) { cmd = readl(gp->regs + MIF_FRAME); if (cmd & MIF_FRAME_TALSB) break; udelay(10); }}static inline void phy_write(struct gem *gp, int reg, u16 val){ __phy_write(gp, reg, val, gp->mii_phy_addr);}static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits){ if (netif_msg_intr(gp)) printk(KERN_DEBUG "%s: mif interrupt\n", gp->dev->name);}static int gem_pcs_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status){ u32 pcs_istat = readl(gp->regs + PCS_ISTAT); u32 pcs_miistat; if (netif_msg_intr(gp)) printk(KERN_DEBUG "%s: pcs interrupt, pcs_istat: 0x%x\n", gp->dev->name, pcs_istat); if (!(pcs_istat & PCS_ISTAT_LSC)) { printk(KERN_ERR "%s: PCS irq but no link status change???\n", dev->name); return 0; } /* The link status bit latches on zero, so you must * read it twice in such a case to see a transition * to the link being up. */ pcs_miistat = readl(gp->regs + PCS_MIISTAT); if (!(pcs_miistat & PCS_MIISTAT_LS)) pcs_miistat |= (readl(gp->regs + PCS_MIISTAT) & PCS_MIISTAT_LS); if (pcs_miistat & PCS_MIISTAT_ANC) { /* The remote-fault indication is only valid * when autoneg has completed. */ if (pcs_miistat & PCS_MIISTAT_RF) printk(KERN_INFO "%s: PCS AutoNEG complete, " "RemoteFault\n", dev->name); else printk(KERN_INFO "%s: PCS AutoNEG complete.\n", dev->name); } if (pcs_miistat & PCS_MIISTAT_LS) { printk(KERN_INFO "%s: PCS link is now up.\n", dev->name); } else { printk(KERN_INFO "%s: PCS link is now down.\n", dev->name); /* If this happens and the link timer is not running, * reset so we re-negotiate. */ if (!timer_pending(&gp->link_timer)) return 1; } return 0;}static int gem_txmac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status){ u32 txmac_stat = readl(gp->regs + MAC_TXSTAT); if (netif_msg_intr(gp)) printk(KERN_DEBUG "%s: txmac interrupt, txmac_stat: 0x%x\n", gp->dev->name, txmac_stat); /* Defer timer expiration is quite normal, * don't even log the event. */ if ((txmac_stat & MAC_TXSTAT_DTE) && !(txmac_stat & ~MAC_TXSTAT_DTE)) return 0; if (txmac_stat & MAC_TXSTAT_URUN) { printk(KERN_ERR "%s: TX MAC xmit underrun.\n", dev->name); gp->net_stats.tx_fifo_errors++; } if (txmac_stat & MAC_TXSTAT_MPE) { printk(KERN_ERR "%s: TX MAC max packet size error.\n", dev->name); gp->net_stats.tx_errors++; } /* The rest are all cases of one of the 16-bit TX * counters expiring. */ if (txmac_stat & MAC_TXSTAT_NCE) gp->net_stats.collisions += 0x10000; if (txmac_stat & MAC_TXSTAT_ECE) { gp->net_stats.tx_aborted_errors += 0x10000; gp->net_stats.collisions += 0x10000; } if (txmac_stat & MAC_TXSTAT_LCE) { gp->net_stats.tx_aborted_errors += 0x10000; gp->net_stats.collisions += 0x10000; } /* We do not keep track of MAC_TXSTAT_FCE and * MAC_TXSTAT_PCE events. */ return 0;}static int gem_rxmac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status){ u32 rxmac_stat = readl(gp->regs + MAC_RXSTAT); int ret = 0; if (netif_msg_intr(gp)) printk(KERN_DEBUG "%s: rxmac interrupt, rxmac_stat: 0x%x\n", gp->dev->name, rxmac_stat); if (rxmac_stat & MAC_RXSTAT_OFLW) { u32 smac = readl(gp->regs + MAC_SMACHINE); printk(KERN_ERR "%s: RX MAC fifo overflow smac[%08x].\n", dev->name, smac); gp->net_stats.rx_over_errors++; gp->net_stats.rx_fifo_errors++; if (((smac >> 24) & 0x7) == 0x7) { /* Due to a bug, the chip is hung in this case * and a full reset is necessary. */ ret = 1; } } if (rxmac_stat & MAC_RXSTAT_ACE) gp->net_stats.rx_frame_errors += 0x10000; if (rxmac_stat & MAC_RXSTAT_CCE) gp->net_stats.rx_crc_errors += 0x10000; if (rxmac_stat & MAC_RXSTAT_LCE) gp->net_stats.rx_length_errors += 0x10000; /* We do not track MAC_RXSTAT_FCE and MAC_RXSTAT_VCE * events. */ return ret;}static int gem_mac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status){ u32 mac_cstat = readl(gp->regs + MAC_CSTAT); if (netif_msg_intr(gp)) printk(KERN_DEBUG "%s: mac interrupt, mac_cstat: 0x%x\n", gp->dev->name, mac_cstat); /* This interrupt is just for pause frame and pause * tracking. It is useful for diagnostics and debug * but probably by default we will mask these events. */ if (mac_cstat & MAC_CSTAT_PS) gp->pause_entered++; if (mac_cstat & MAC_CSTAT_PRCV) gp->pause_last_time_recvd = (mac_cstat >> 16); return 0;}static int gem_mif_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status){ u32 mif_status = readl(gp->regs + MIF_STATUS); u32 reg_val, changed_bits; reg_val = (mif_status & MIF_STATUS_DATA) >> 16; changed_bits = (mif_status & MIF_STATUS_STAT); gem_handle_mif_event(gp, reg_val, changed_bits); return 0;}static int gem_pci_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status){ u32 pci_estat = readl(gp->regs + GREG_PCIESTAT); if (gp->pdev->vendor == PCI_VENDOR_ID_SUN && gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) { printk(KERN_ERR "%s: PCI error [%04x] ", dev->name, pci_estat); if (pci_estat & GREG_PCIESTAT_BADACK) printk("<No ACK64# during ABS64 cycle> "); if (pci_estat & GREG_PCIESTAT_DTRTO) printk("<Delayed transaction timeout> "); if (pci_estat & GREG_PCIESTAT_OTHER) printk("<other>"); printk("\n"); } else { pci_estat |= GREG_PCIESTAT_OTHER; printk(KERN_ERR "%s: PCI error\n", dev->name); } if (pci_estat & GREG_PCIESTAT_OTHER) { u16 pci_cfg_stat; /* Interrogate PCI config space for the * true cause. */ pci_read_config_word(gp->pdev, PCI_STATUS, &pci_cfg_stat); printk(KERN_ERR "%s: Read PCI cfg space status [%04x]\n", dev->name, pci_cfg_stat); if (pci_cfg_stat & PCI_STATUS_PARITY) printk(KERN_ERR "%s: PCI parity error detected.\n", dev->name); if (pci_cfg_stat & PCI_STATUS_SIG_TARGET_ABORT) printk(KERN_ERR "%s: PCI target abort.\n", dev->name); if (pci_cfg_stat & PCI_STATUS_REC_TARGET_ABORT) printk(KERN_ERR "%s: PCI master acks target abort.\n", dev->name); if (pci_cfg_stat & PCI_STATUS_REC_MASTER_ABORT) printk(KERN_ERR "%s: PCI master abort.\n", dev->name); if (pci_cfg_stat & PCI_STATUS_SIG_SYSTEM_ERROR) printk(KERN_ERR "%s: PCI system error SERR#.\n", dev->name); if (pci_cfg_stat & PCI_STATUS_DETECTED_PARITY) printk(KERN_ERR "%s: PCI parity error.\n", dev->name); /* Write the error bits back to clear them. */ pci_cfg_stat &= (PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); pci_write_config_word(gp->pdev, PCI_STATUS, pci_cfg_stat); } /* For all PCI errors, we should reset the chip. */ return 1;}/* All non-normal interrupt conditions get serviced here. * Returns non-zero if we should just exit the interrupt * handler right now (ie. if we reset the card which invalidates * all of the other original irq status bits). */static int gem_abnormal_irq(struct net_device *dev, struct gem *gp, u32 gem_status){ if (gem_status & GREG_STAT_RXNOBUF) { /* Frame arrived, no free RX buffers available. */ if (netif_msg_rx_err(gp)) printk(KERN_DEBUG "%s: no buffer for rx frame\n", gp->dev->name); gp->net_stats.rx_dropped++; } if (gem_status & GREG_STAT_RXTAGERR) { /* corrupt RX tag framing */ if (netif_msg_rx_err(gp)) printk(KERN_DEBUG "%s: corrupt rx tag framing\n", gp->dev->name); gp->net_stats.rx_errors++; goto do_reset; } if (gem_status & GREG_STAT_PCS) { if (gem_pcs_interrupt(dev, gp, gem_status)) goto do_reset; } if (gem_status & GREG_STAT_TXMAC) { if (gem_txmac_interrupt(dev, gp, gem_status)) goto do_reset; } if (gem_status & GREG_STAT_RXMAC) { if (gem_rxmac_interrupt(dev, gp, gem_status)) goto do_reset; } if (gem_status & GREG_STAT_MAC) { if (gem_mac_interrupt(dev, gp, gem_status)) goto do_reset; } if (gem_status & GREG_STAT_MIF) { if (gem_mif_interrupt(dev, gp, gem_status)) goto do_reset; } if (gem_status & GREG_STAT_PCIERR) { if (gem_pci_interrupt(dev, gp, gem_status)) goto do_reset; } return 0;do_reset: gp->reset_task_pending = 2; schedule_task(&gp->reset_task); return 1;}static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_status){ int entry, limit; if (netif_msg_intr(gp)) printk(KERN_DEBUG "%s: tx interrupt, gem_status: 0x%x\n", gp->dev->name, gem_status); entry = gp->tx_old; limit = ((gem_status & GREG_STAT_TXNR) >> GREG_STAT_TXNR_SHIFT); while (entry != limit) { struct sk_buff *skb; struct gem_txd *txd; dma_addr_t dma_addr; u32 dma_len; int frag; if (netif_msg_tx_done(gp)) printk(KERN_DEBUG "%s: tx done, slot %d\n", gp->dev->name, entry); skb = gp->tx_skbs[entry]; if (skb_shinfo(skb)->nr_frags) { int last = entry + skb_shinfo(skb)->nr_frags; int walk = entry; int incomplete = 0; last &= (TX_RING_SIZE - 1); for (;;) { walk = NEXT_TX(walk); if (walk == limit) incomplete = 1; if (walk == last) break; } if (incomplete)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -