diff options
Diffstat (limited to 'drivers/net/sungem.c')
-rw-r--r-- | drivers/net/sungem.c | 3204 |
1 files changed, 3204 insertions, 0 deletions
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c new file mode 100644 index 00000000000..5cd50fd53c1 --- /dev/null +++ b/drivers/net/sungem.c @@ -0,0 +1,3204 @@ +/* $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 netif_poll_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/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 <asm/system.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_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) + +#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 }, + {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); + + /* 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 = 1; + schedule_work(&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) + break; + } + gp->tx_skbs[entry] = NULL; + gp->net_stats.tx_bytes += skb->len; + + for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { + txd = &gp->init_block->txd[entry]; + + dma_addr = le64_to_cpu(txd->buffer); + dma_len = le64_to_cpu(txd->control_word) & TXDCTRL_BUFSZ; + + pci_unmap_page(gp->pdev, dma_addr, dma_len, PCI_DMA_TODEVICE); + entry = NEXT_TX(entry); + } + + gp->net_stats.tx_packets++; + dev_kfree_skb_irq(skb); + } + gp->tx_old = entry; + + if (netif_queue_stopped(dev) && + TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1)) + netif_wake_queue(dev); +} + +static __inline__ void gem_post_rxds(struct gem *gp, int limit) +{ + int cluster_start, curr, count, kick; + + cluster_start = curr = (gp->rx_new & ~(4 - 1)); + count = 0; + kick = -1; + wmb(); + while (curr != limit) { + curr = NEXT_RX(curr); + if (++count == 4) { + struct gem_rxd *rxd = + &gp->init_block->rxd[cluster_start]; + for (;;) { + rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp)); + rxd++; + cluster_start = NEXT_RX(cluster_start); + if (cluster_start == curr) + break; + } + kick = curr; + count = 0; + } + } + if (kick >= 0) { + mb(); + writel(kick, gp->regs + RXDMA_KICK); + } +} + +static int gem_rx(struct gem *gp, int work_to_do) +{ + int entry, drops, work_done = 0; + u32 done; + + if (netif_msg_rx_status(gp)) + printk(KERN_DEBUG "%s: rx interrupt, done: %d, rx_new: %d\n", + gp->dev->name, readl(gp->regs + RXDMA_DONE), gp->rx_new); + + entry = gp->rx_new; + drops = 0; + done = readl(gp->regs + RXDMA_DONE); + for (;;) { + struct gem_rxd *rxd = &gp->init_block->rxd[entry]; + struct sk_buff *skb; + u64 status = cpu_to_le64(rxd->status_word); + dma_addr_t dma_addr; + int len; + + if ((status & RXDCTRL_OWN) != 0) + break; + + if (work_done >= RX_RING_SIZE || work_done >= work_to_do) + break; + + /* When writing back RX descriptor, GEM writes status + * then buffer address, possibly in seperate transactions. + * If we don't wait for the chip to write both, we could + * post a new buffer to this descriptor then have GEM spam + * on the buffer address. We sync on the RX completion + * register to prevent this from happening. + */ + if (entry == done) { + done = readl(gp->regs + RXDMA_DONE); + if (entry == done) + break; + } + + /* We can now account for the work we're about to do */ + work_done++; + + skb = gp->rx_skbs[entry]; + + len = (status & RXDCTRL_BUFSZ) >> 16; + if ((len < ETH_ZLEN) || (status & RXDCTRL_BAD)) { + gp->net_stats.rx_errors++; + if (len < ETH_ZLEN) + gp->net_stats.rx_length_errors++; + if (len & RXDCTRL_BAD) + gp->net_stats.rx_crc_errors++; + + /* We'll just return it to GEM. */ + drop_it: + gp->net_stats.rx_dropped++; + goto next; + } + + dma_addr = cpu_to_le64(rxd->buffer); + if (len > RX_COPY_THRESHOLD) { + struct sk_buff *new_skb; + + new_skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC); + if (new_skb == NULL) { + drops++; + goto drop_it; + } + pci_unmap_page(gp->pdev, dma_addr, + RX_BUF_ALLOC_SIZE(gp), + PCI_DMA_FROMDEVICE); + gp->rx_skbs[entry] = new_skb; + new_skb->dev = gp->dev; + skb_put(new_skb, (gp->rx_buf_sz + RX_OFFSET)); + rxd->buffer = cpu_to_le64(pci_map_page(gp->pdev, + virt_to_page(new_skb->data), + offset_in_page(new_skb->data), + RX_BUF_ALLOC_SIZE(gp), + PCI_DMA_FROMDEVICE)); + skb_reserve(new_skb, RX_OFFSET); + + /* Trim the original skb for the netif. */ + skb_trim(skb, len); + } else { + struct sk_buff *copy_skb = dev_alloc_skb(len + 2); + + if (copy_skb == NULL) { + drops++; + goto drop_it; + } + + copy_skb->dev = gp->dev; + skb_reserve(copy_skb, 2); + skb_put(copy_skb, len); + pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); + memcpy(copy_skb->data, skb->data, len); + pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); + + /* We'll reuse the original ring buffer. */ + skb = copy_skb; + } + + skb->csum = ntohs((status & RXDCTRL_TCPCSUM) ^ 0xffff); + skb->ip_summed = CHECKSUM_HW; + skb->protocol = eth_type_trans(skb, gp->dev); + + netif_receive_skb(skb); + + gp->net_stats.rx_packets++; + gp->net_stats.rx_bytes += len; + gp->dev->last_rx = jiffies; + + next: + entry = NEXT_RX(entry); + } + + gem_post_rxds(gp, entry); + + gp->rx_new = entry; + + if (drops) + printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", + gp->dev->name); + + return work_done; +} + +static int gem_poll(struct net_device *dev, int *budget) +{ + struct gem *gp = dev->priv; + unsigned long flags; + + /* + * NAPI locking nightmare: See comment at head of driver + */ + spin_lock_irqsave(&gp->lock, flags); + + do { + int work_to_do, work_done; + + /* Handle anomalies */ + if (gp->status & GREG_STAT_ABNORMAL) { + if (gem_abnormal_irq(dev, gp, gp->status)) + break; + } + + /* Run TX completion thread */ + spin_lock(&gp->tx_lock); + gem_tx(dev, gp, gp->status); + spin_unlock(&gp->tx_lock); + + spin_unlock_irqrestore(&gp->lock, flags); + + /* Run RX thread. We don't use any locking here, + * code willing to do bad things - like cleaning the + * rx ring - must call netif_poll_disable(), which + * schedule_timeout()'s if polling is already disabled. + */ + work_to_do = min(*budget, dev->quota); + + work_done = gem_rx(gp, work_to_do); + + *budget -= work_done; + dev->quota -= work_done; + + if (work_done >= work_to_do) + return 1; + + spin_lock_irqsave(&gp->lock, flags); + + gp->status = readl(gp->regs + GREG_STAT); + } while (gp->status & GREG_STAT_NAPI); + + __netif_rx_complete(dev); + gem_enable_ints(gp); + + spin_unlock_irqrestore(&gp->lock, flags); + return 0; +} + +static irqreturn_t gem_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct gem *gp = dev->priv; + unsigned long flags; + + /* Swallow interrupts when shutting the chip down, though + * that shouldn't happen, we should have done free_irq() at + * this point... + */ + if (!gp->running) + return IRQ_HANDLED; + + spin_lock_irqsave(&gp->lock, flags); + + if (netif_rx_schedule_prep(dev)) { + u32 gem_status = readl(gp->regs + GREG_STAT); + + if (gem_status == 0) { + spin_unlock_irqrestore(&gp->lock, flags); + return IRQ_NONE; + } + gp->status = gem_status; + gem_disable_ints(gp); + __netif_rx_schedule(dev); + } + + spin_unlock_irqrestore(&gp->lock, flags); + + /* If polling was disabled at the time we received that + * interrupt, we may return IRQ_HANDLED here while we + * should return IRQ_NONE. No big deal... + */ + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void gem_poll_controller(struct net_device *dev) +{ + /* gem_interrupt is safe to reentrance so no need + * to disable_irq here. + */ + gem_interrupt(dev->irq, dev, NULL); +} +#endif + +static void gem_tx_timeout(struct net_device *dev) +{ + struct gem *gp = dev->priv; + + printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name); + if (!gp->running) { + printk("%s: hrm.. hw not running !\n", dev->name); + return; + } + printk(KERN_ERR "%s: TX_STATE[%08x:%08x:%08x]\n", + dev->name, + readl(gp->regs + TXDMA_CFG), + readl(gp->regs + MAC_TXSTAT), + readl(gp->regs + MAC_TXCFG)); + printk(KERN_ERR "%s: RX_STATE[%08x:%08x:%08x]\n", + dev->name, + readl(gp->regs + RXDMA_CFG), + readl(gp->regs + MAC_RXSTAT), + readl(gp->regs + MAC_RXCFG)); + + spin_lock_irq(&gp->lock); + spin_lock(&gp->tx_lock); + + gp->reset_task_pending = 1; + schedule_work(&gp->reset_task); + + spin_unlock(&gp->tx_lock); + spin_unlock_irq(&gp->lock); +} + +static __inline__ int gem_intme(int entry) +{ + /* Algorithm: IRQ every 1/2 of descriptors. */ + if (!(entry & ((TX_RING_SIZE>>1)-1))) + return 1; + + return 0; +} + +static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct gem *gp = dev->priv; + int entry; + u64 ctrl; + unsigned long flags; + + ctrl = 0; + if (skb->ip_summed == CHECKSUM_HW) { + u64 csum_start_off, csum_stuff_off; + + csum_start_off = (u64) (skb->h.raw - skb->data); + csum_stuff_off = (u64) ((skb->h.raw + skb->csum) - skb->data); + + ctrl = (TXDCTRL_CENAB | + (csum_start_off << 15) | + (csum_stuff_off << 21)); + } + + local_irq_save(flags); + if (!spin_trylock(&gp->tx_lock)) { + /* Tell upper layer to requeue */ + local_irq_restore(flags); + return NETDEV_TX_LOCKED; + } + /* We raced with gem_do_stop() */ + if (!gp->running) { + spin_unlock_irqrestore(&gp->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + /* This is a hard error, log it. */ + if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(dev); + spin_unlock_irqrestore(&gp->tx_lock, flags); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + entry = gp->tx_new; + gp->tx_skbs[entry] = skb; + + if (skb_shinfo(skb)->nr_frags == 0) { + struct gem_txd *txd = &gp->init_block->txd[entry]; + dma_addr_t mapping; + u32 len; + + len = skb->len; + mapping = pci_map_page(gp->pdev, + virt_to_page(skb->data), + offset_in_page(skb->data), + len, PCI_DMA_TODEVICE); + ctrl |= TXDCTRL_SOF | TXDCTRL_EOF | len; + if (gem_intme(entry)) + ctrl |= TXDCTRL_INTME; + txd->buffer = cpu_to_le64(mapping); + wmb(); + txd->control_word = cpu_to_le64(ctrl); + entry = NEXT_TX(entry); + } else { + struct gem_txd *txd; + u32 first_len; + u64 intme; + dma_addr_t first_mapping; + int frag, first_entry = entry; + + intme = 0; + if (gem_intme(entry)) + intme |= TXDCTRL_INTME; + + /* We must give this initial chunk to the device last. + * Otherwise we could race with the device. + */ + first_len = skb_headlen(skb); + first_mapping = pci_map_page(gp->pdev, virt_to_page(skb->data), + offset_in_page(skb->data), + first_len, PCI_DMA_TODEVICE); + entry = NEXT_TX(entry); + + for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { + skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; + u32 len; + dma_addr_t mapping; + u64 this_ctrl; + + len = this_frag->size; + mapping = pci_map_page(gp->pdev, + this_frag->page, + this_frag->page_offset, + len, PCI_DMA_TODEVICE); + this_ctrl = ctrl; + if (frag == skb_shinfo(skb)->nr_frags - 1) + this_ctrl |= TXDCTRL_EOF; + + txd = &gp->init_block->txd[entry]; + txd->buffer = cpu_to_le64(mapping); + wmb(); + txd->control_word = cpu_to_le64(this_ctrl | len); + + if (gem_intme(entry)) + intme |= TXDCTRL_INTME; + + entry = NEXT_TX(entry); + } + txd = &gp->init_block->txd[first_entry]; + txd->buffer = cpu_to_le64(first_mapping); + wmb(); + txd->control_word = + cpu_to_le64(ctrl | TXDCTRL_SOF | intme | first_len); + } + + gp->tx_new = entry; + if (TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + + if (netif_msg_tx_queued(gp)) + printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n", + dev->name, entry, skb->len); + mb(); + writel(gp->tx_new, gp->regs + TXDMA_KICK); + spin_unlock_irqrestore(&gp->tx_lock, flags); + + dev->trans_start = jiffies; + + return NETDEV_TX_OK; +} + +#define STOP_TRIES 32 + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_reset(struct gem *gp) +{ + int limit; + u32 val; + + /* Make sure we won't get any more interrupts */ + writel(0xffffffff, gp->regs + GREG_IMASK); + + /* Reset the chip */ + writel(gp->swrst_base | GREG_SWRST_TXRST | GREG_SWRST_RXRST, + gp->regs + GREG_SWRST); + + limit = STOP_TRIES; + + do { + udelay(20); + val = readl(gp->regs + GREG_SWRST); + if (limit-- <= 0) + break; + } while (val & (GREG_SWRST_TXRST | GREG_SWRST_RXRST)); + + if (limit <= 0) + printk(KERN_ERR "%s: SW reset is ghetto.\n", gp->dev->name); +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_start_dma(struct gem *gp) +{ + u32 val; + + /* We are ready to rock, turn everything on. */ + val = readl(gp->regs + TXDMA_CFG); + writel(val | TXDMA_CFG_ENABLE, gp->regs + TXDMA_CFG); + val = readl(gp->regs + RXDMA_CFG); + writel(val | RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG); + val = readl(gp->regs + MAC_TXCFG); + writel(val | MAC_TXCFG_ENAB, gp->regs + MAC_TXCFG); + val = readl(gp->regs + MAC_RXCFG); + writel(val | MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG); + + |