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