sungem.c
来自「linux 内核源代码」· C语言 代码 · 共 2,547 行 · 第 1/5 页
C
2,547 行
/* $Id: sungem.c,v 1.44.2.22 2002/03/13 01:18:12 davem Exp $ * sungem.c: Sun GEM ethernet driver. * * Copyright (C) 2000, 2001, 2002, 2003 David S. Miller (davem@redhat.com) * * Support for Apple GMAC and assorted PHYs, WOL, Power Management * (C) 2001,2002,2003 Benjamin Herrenscmidt (benh@kernel.crashing.org) * (C) 2004,2005 Benjamin Herrenscmidt, IBM Corp. * * NAPI and NETPOLL support * (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com) * * TODO: * - Now that the driver was significantly simplified, I need to rework * the locking. I'm sure we don't need _2_ spinlocks, and we probably * can avoid taking most of them for so long period of time (and schedule * instead). The main issues at this point are caused by the netdev layer * though: * * gem_change_mtu() and gem_set_multicast() are called with a read_lock() * help by net/core/dev.c, thus they can't schedule. That means they can't * call napi_disable() neither, thus force gem_poll() to keep a spinlock * where it could have been dropped. change_mtu especially would love also to * be able to msleep instead of horrid locked delays when resetting the HW, * but that read_lock() makes it impossible, unless I defer it's action to * the reset task, which means it'll be asynchronous (won't take effect until * the system schedules a bit). * * Also, it would probably be possible to also remove most of the long-life * locking in open/resume code path (gem_reinit_chip) by beeing more careful * about when we can start taking interrupts or get xmit() called... */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.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/dma-mapping.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/crc32.h>#include <linux/random.h>#include <linux/workqueue.h>#include <linux/if_vlan.h>#include <linux/bitops.h>#include <linux/mutex.h>#include <linux/mm.h>#include <asm/system.h>#include <asm/io.h>#include <asm/byteorder.h>#include <asm/uaccess.h>#include <asm/irq.h>#ifdef CONFIG_SPARC#include <asm/idprom.h>#include <asm/prom.h>#endif#ifdef CONFIG_PPC_PMAC#include <asm/pci-bridge.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/pmac_feature.h>#endif#include "sungem_phy.h"#include "sungem.h"/* Stripping FCS is causing problems, disabled for now */#undef STRIP_FCS#define DEFAULT_MSG (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK)#define ADVERTISE_MASK (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | \ SUPPORTED_Pause | SUPPORTED_Autoneg)#define DRV_NAME "sungem"#define DRV_VERSION "0.98"#define DRV_RELDATE "8/24/03"#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");#define GEM_MODULE_NAME "gem"#define PFX GEM_MODULE_NAME ": "static struct pci_device_id gem_pci_tbl[] = { { 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 }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID2_GMAC, 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 phy_addr, int reg){ 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 int _phy_read(struct net_device *dev, int mii_id, int reg){ struct gem *gp = dev->priv; return __phy_read(gp, mii_id, reg);}static inline u16 phy_read(struct gem *gp, int reg){ return __phy_read(gp, gp->mii_phy_addr, reg);}static void __phy_write(struct gem *gp, int phy_addr, int reg, u16 val){ 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 net_device *dev, int mii_id, int reg, int val){ struct gem *gp = dev->priv; __phy_write(gp, mii_id, reg, val & 0xffff);}static inline void phy_write(struct gem *gp, int reg, u16 val){ __phy_write(gp, gp->mii_phy_addr, reg, val);}static inline void gem_enable_ints(struct gem *gp){ /* Enable all interrupts but TXDONE */ writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK);}static inline void gem_disable_ints(struct gem *gp){ /* Disable all interrupts, including TXDONE */ writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);}static void gem_get_cell(struct gem *gp){ BUG_ON(gp->cell_enabled < 0); gp->cell_enabled++;#ifdef CONFIG_PPC_PMAC if (gp->cell_enabled == 1) { mb(); pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1); udelay(10); }#endif /* CONFIG_PPC_PMAC */}/* Turn off the chip's clock */static void gem_put_cell(struct gem *gp){ BUG_ON(gp->cell_enabled <= 0); gp->cell_enabled--;#ifdef CONFIG_PPC_PMAC if (gp->cell_enabled == 0) { mb(); pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0); udelay(10); }#endif /* CONFIG_PPC_PMAC */}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); netif_carrier_on(gp->dev); } else { printk(KERN_INFO "%s: PCS link is now down.\n", dev->name); netif_carrier_off(gp->dev); /* 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;}/* When we get a RX fifo overflow, the RX unit in GEM is probably hung * so we do the following. * * If any part of the reset goes wrong, we return 1 and that causes the * whole chip to be reset. */static int gem_rxmac_reset(struct gem *gp){ struct net_device *dev = gp->dev; int limit, i; u64 desc_dma; u32 val; /* First, reset & disable MAC RX. */ writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST); for (limit = 0; limit < 5000; limit++) { if (!(readl(gp->regs + MAC_RXRST) & MAC_RXRST_CMD)) break; udelay(10); } if (limit == 5000) { printk(KERN_ERR "%s: RX MAC will not reset, resetting whole " "chip.\n", dev->name); return 1; } writel(gp->mac_rx_cfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG); for (limit = 0; limit < 5000; limit++) { if (!(readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB)) break; udelay(10); } if (limit == 5000) { printk(KERN_ERR "%s: RX MAC will not disable, resetting whole " "chip.\n", dev->name); return 1; } /* Second, disable RX DMA. */ writel(0, gp->regs + RXDMA_CFG); for (limit = 0; limit < 5000; limit++) { if (!(readl(gp->regs + RXDMA_CFG) & RXDMA_CFG_ENABLE)) break; udelay(10); } if (limit == 5000) { printk(KERN_ERR "%s: RX DMA will not disable, resetting whole " "chip.\n", dev->name); return 1; } udelay(5000); /* Execute RX reset command. */ writel(gp->swrst_base | GREG_SWRST_RXRST, gp->regs + GREG_SWRST); for (limit = 0; limit < 5000; limit++) { if (!(readl(gp->regs + GREG_SWRST) & GREG_SWRST_RXRST)) break; udelay(10); } if (limit == 5000) { printk(KERN_ERR "%s: RX reset command will not execute, resetting " "whole chip.\n", dev->name); return 1; } /* Refresh the RX ring. */ for (i = 0; i < RX_RING_SIZE; i++) { struct gem_rxd *rxd = &gp->init_block->rxd[i]; if (gp->rx_skbs[i] == NULL) { printk(KERN_ERR "%s: Parts of RX ring empty, resetting " "whole chip.\n", dev->name); return 1; } rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp)); } gp->rx_new = gp->rx_old = 0; /* Now we must reprogram the rest of RX unit. */ desc_dma = (u64) gp->gblock_dvma; desc_dma += (INIT_BLOCK_TX_RING_SIZE * sizeof(struct gem_txd)); writel(desc_dma >> 32, gp->regs + RXDMA_DBHI); writel(desc_dma & 0xffffffff, gp->regs + RXDMA_DBLOW); writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK); val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) | ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128); writel(val, gp->regs + RXDMA_CFG); if (readl(gp->regs + GREG_BIFCFG) & GREG_BIFCFG_M66EN) writel(((5 & RXDMA_BLANK_IPKTS) | ((8 << 12) & RXDMA_BLANK_ITIME)), gp->regs + RXDMA_BLANK); else writel(((5 & RXDMA_BLANK_IPKTS) | ((4 << 12) & RXDMA_BLANK_ITIME)), gp->regs + RXDMA_BLANK); val = (((gp->rx_pause_off / 64) << 0) & RXDMA_PTHRESH_OFF); val |= (((gp->rx_pause_on / 64) << 12) & RXDMA_PTHRESH_ON); writel(val, gp->regs + RXDMA_PTHRESH); val = readl(gp->regs + RXDMA_CFG); writel(val | RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG); writel(MAC_RXSTAT_RCV, gp->regs + MAC_RXMASK); val = readl(gp->regs + MAC_RXCFG); writel(val | MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG); 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++; ret = gem_rxmac_reset(gp); } 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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?