diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/sungem.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
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); + + (void) readl(gp->regs + MAC_RXCFG); + udelay(100); + + gem_enable_ints(gp); + + writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK); +} + +/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be + * actually stopped before about 4ms tho ... + */ +static void gem_stop_dma(struct gem *gp) +{ + u32 val; + + /* We are done rocking, turn everything off. */ + 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); + + (void) readl(gp->regs + MAC_RXCFG); + + /* Need to wait a bit ... done by the caller */ +} + + +/* Must be invoked under gp->lock and gp->tx_lock. */ +// XXX dbl check what that function should do when called on PCS PHY +static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep) +{ + u32 advertise, features; + int autoneg; + int speed; + int duplex; + + if (gp->phy_type != phy_mii_mdio0 && + gp->phy_type != phy_mii_mdio1) + goto non_mii; + + /* Setup advertise */ + if (found_mii_phy(gp)) + features = gp->phy_mii.def->features; + else + features = 0; + + advertise = features & ADVERTISE_MASK; + if (gp->phy_mii.advertising != 0) + advertise &= gp->phy_mii.advertising; + + autoneg = gp->want_autoneg; + speed = gp->phy_mii.speed; + duplex = gp->phy_mii.duplex; + + /* Setup link parameters */ + if (!ep) + goto start_aneg; + if (ep->autoneg == AUTONEG_ENABLE) { + advertise = ep->advertising; + autoneg = 1; + } else { + autoneg = 0; + speed = ep->speed; + duplex = ep->duplex; + } + +start_aneg: + /* Sanitize settings based on PHY capabilities */ + if ((features & SUPPORTED_Autoneg) == 0) + autoneg = 0; + if (speed == SPEED_1000 && + !(features & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full))) + speed = SPEED_100; + if (speed == SPEED_100 && + !(features & (SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full))) + speed = SPEED_10; + if (duplex == DUPLEX_FULL && + !(features & (SUPPORTED_1000baseT_Full | + SUPPORTED_100baseT_Full | + SUPPORTED_10baseT_Full))) + duplex = DUPLEX_HALF; + if (speed == 0) + speed = SPEED_10; + + /* If we are asleep, we don't try to actually setup the PHY, we + * just store the settings + */ + if (gp->asleep) { + gp->phy_mii.autoneg = gp->want_autoneg = autoneg; + gp->phy_mii.speed = speed; + gp->phy_mii.duplex = duplex; + return; + } + + /* Configure PHY & start aneg */ + gp->want_autoneg = autoneg; + if (autoneg) { + if (found_mii_phy(gp)) + gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, advertise); + gp->lstate = link_aneg; + } else { + if (found_mii_phy(gp)) + gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, speed, duplex); + gp->lstate = link_force_ok; + } + +non_mii: + gp->timer_ticks = 0; + mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10)); +} + +/* A link-up condition has occurred, initialize and enable the + * rest of the chip. + * + * Must be invoked under gp->lock and gp->tx_lock. + */ +static int gem_set_link_modes(struct gem *gp) +{ + u32 val; + int full_duplex, speed, pause; + + full_duplex = 0; + speed = SPEED_10; + pause = 0; + + if (found_mii_phy(gp)) { + if (gp->phy_mii.def->ops->read_link(&gp->phy_mii)) + return 1; + full_duplex = (gp->phy_mii.duplex == DUPLEX_FULL); + speed = gp->phy_mii.speed; + pause = gp->phy_mii.pause; + } else if (gp->phy_type == phy_serialink || + gp->phy_type == phy_serdes) { + u32 pcs_lpa = readl(gp->regs + PCS_MIILP); + + if (pcs_lpa & PCS_MIIADV_FD) + full_duplex = 1; + speed = SPEED_1000; + } + + if (netif_msg_link(gp)) + printk(KERN_INFO "%s: Link is up at %d Mbps, %s-duplex.\n", + gp->dev->name, speed, (full_duplex ? "full" : "half")); + + if (!gp->running) + return 0; + + val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU); + if (full_duplex) { + val |= (MAC_TXCFG_ICS | MAC_TXCFG_ICOLL); + } else { + /* MAC_TXCFG_NBO must be zero. */ + } + writel(val, gp->regs + MAC_TXCFG); + + val = (MAC_XIFCFG_OE | MAC_XIFCFG_LLED); + if (!full_duplex && + (gp->phy_type == phy_mii_mdio0 || + gp->phy_type == phy_mii_mdio1)) { + val |= MAC_XIFCFG_DISE; + } else if (full_duplex) { + val |= MAC_XIFCFG_FLED; + } + + if (speed == SPEED_1000) + val |= (MAC_XIFCFG_GMII); + + writel(val, gp->regs + MAC_XIFCFG); + + /* If gigabit and half-duplex, enable carrier extension + * mode. Else, disable it. + */ + if (speed == SPEED_1000 && !full_duplex) { + val = readl(gp->regs + MAC_TXCFG); + writel(val | MAC_TXCFG_TCE, gp->regs + MAC_TXCFG); + + val = readl(gp->regs + MAC_RXCFG); + writel(val | MAC_RXCFG_RCE, gp->regs + MAC_RXCFG); + } else { + val = readl(gp->regs + MAC_TXCFG); + writel(val & ~MAC_TXCFG_TCE, gp->regs + MAC_TXCFG); + + val = readl(gp->regs + MAC_RXCFG); + writel(val & ~MAC_RXCFG_RCE, gp->regs + MAC_RXCFG); + } + + if (gp->phy_type == phy_serialink || + gp->phy_type == phy_serdes) { + u32 pcs_lpa = readl(gp->regs + PCS_MIILP); + + if (pcs_lpa & (PCS_MIIADV_SP | PCS_MIIADV_AP)) + pause = 1; + } + + if (netif_msg_link(gp)) { + if (pause) { + printk(KERN_INFO "%s: Pause is enabled " + "(rxfifo: %d off: %d on: %d)\n", + gp->dev->name, + gp->rx_fifo_sz, + gp->rx_pause_off, + gp->rx_pause_on); + } else { + printk(KERN_INFO "%s: Pause is disabled\n", + gp->dev->name); + } + } + + if (!full_duplex) + writel(512, gp->regs + MAC_STIME); + else + writel(64, gp->regs + MAC_STIME); + val = readl(gp->regs + MAC_MCCFG); + if (pause) + val |= (MAC_MCCFG_SPE | MAC_MCCFG_RPE); + else + val &= ~(MAC_MCCFG_SPE | MAC_MCCFG_RPE); + writel(val, gp->regs + MAC_MCCFG); + + gem_start_dma(gp); + + return 0; +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static int gem_mdio_link_not_up(struct gem *gp) +{ + switch (gp->lstate) { + case link_force_ret: + if (netif_msg_link(gp)) + printk(KERN_INFO "%s: Autoneg failed again, keeping" + " forced mode\n", gp->dev->name); + gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, + gp->last_forced_speed, DUPLEX_HALF); + gp->timer_ticks = 5; + gp->lstate = link_force_ok; + return 0; + case link_aneg: + /* We try forced modes after a failed aneg only on PHYs that don't + * have "magic_aneg" bit set, which means they internally do the + * while forced-mode thingy. On these, we just restart aneg + */ + if (gp->phy_mii.def->magic_aneg) + return 1; + if (netif_msg_link(gp)) + printk(KERN_INFO "%s: switching to forced 100bt\n", + gp->dev->name); + /* Try forced modes. */ + gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, SPEED_100, + DUPLEX_HALF); + gp->timer_ticks = 5; + gp->lstate = link_force_try; + return 0; + case link_force_try: + /* Downgrade from 100 to 10 Mbps if necessary. + * If already at 10Mbps, warn user about the + * situation every 10 ticks. + */ + if (gp->phy_mii.speed == SPEED_100) { + gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, SPEED_10, + DUPLEX_HALF); + gp->timer_ticks = 5; + if (netif_msg_link(gp)) + printk(KERN_INFO "%s: switching to forced 10bt\n", + gp->dev->name); + return 0; + } else + return 1; + default: + return 0; + } +} + +static void gem_link_timer(unsigned long data) +{ + struct gem *gp = (struct gem *) data; + int restart_aneg = 0; + + if (gp->asleep) + return; + + spin_lock_irq(&gp->lock); + spin_lock(&gp->tx_lock); + gem_get_cell(gp); + + /* If the reset task is still pending, we just + * reschedule the link timer + */ + if (gp->reset_task_pending) + goto restart; + + if (gp->phy_type == phy_serialink || + gp->phy_type == phy_serdes) { + u32 val = readl(gp->regs + PCS_MIISTAT); + + if (!(val & PCS_MIISTAT_LS)) + val = readl(gp->regs + PCS_MIISTAT); + + if ((val & PCS_MIISTAT_LS) != 0) { + gp->lstate = link_up; + netif_carrier_on(gp->dev); + (void)gem_set_link_modes(gp); + } + goto restart; + } + if (found_mii_phy(gp) && gp->phy_mii.def->ops->poll_link(&gp->phy_mii)) { + /* Ok, here we got a link. If we had it due to a forced + * fallback, and we were configured for autoneg, we do + * retry a short autoneg pass. If you know your hub is + * broken, use ethtool ;) + */ + if (gp->lstate == link_force_try && gp->want_autoneg) { + gp->lstate = link_force_ret; + gp->last_forced_speed = gp->phy_mii.speed; + gp->timer_ticks = 5; + if (netif_msg_link(gp)) + printk(KERN_INFO "%s: Got link after fallback, retrying" + " autoneg once...\n", gp->dev->name); + gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, gp->phy_mii.advertising); + } else if (gp->lstate != link_up) { + gp->lstate = link_up; + netif_carrier_on(gp->dev); + if (gem_set_link_modes(gp)) + restart_aneg = 1; + } + } else { + /* If the link was previously up, we restart the + * whole process + */ + if (gp->lstate == link_up) { + gp->lstate = link_down; + if (netif_msg_link(gp)) + printk(KERN_INFO "%s: Link down\n", + gp->dev->name); + netif_carrier_off(gp->dev); + gp->reset_task_pending = 1; + schedule_work(&gp->reset_task); + restart_aneg = 1; + } else if (++gp->timer_ticks > 10) { + if (found_mii_phy(gp)) + restart_aneg = gem_mdio_link_not_up(gp); + else + restart_aneg = 1; + } + } + if (restart_aneg) { + gem_begin_auto_negotiation(gp, NULL); + goto out_unlock; + } +restart: + mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10)); +out_unlock: + gem_put_cell(gp); + spin_unlock(&gp->tx_lock); + spin_unlock_irq(&gp->lock); +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_clean_rings(struct gem *gp) +{ + struct gem_init_block *gb = gp->init_block; + struct sk_buff *skb; + int i; + dma_addr_t dma_addr; + + for (i = 0; i < RX_RING_SIZE; i++) { + struct gem_rxd *rxd; + + rxd = &gb->rxd[i]; + if (gp->rx_skbs[i] != NULL) { + skb = gp->rx_skbs[i]; + dma_addr = le64_to_cpu(rxd->buffer); + pci_unmap_page(gp->pdev, dma_addr, + RX_BUF_ALLOC_SIZE(gp), + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + gp->rx_skbs[i] = NULL; + } + rxd->status_word = 0; + wmb(); + rxd->buffer = 0; + } + + for (i = 0; i < TX_RING_SIZE; i++) { + if (gp->tx_skbs[i] != NULL) { + struct gem_txd *txd; + int frag; + + skb = gp->tx_skbs[i]; + gp->tx_skbs[i] = NULL; + + for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { + int ent = i & (TX_RING_SIZE - 1); + + txd = &gb->txd[ent]; + dma_addr = le64_to_cpu(txd->buffer); + pci_unmap_page(gp->pdev, dma_addr, + le64_to_cpu(txd->control_word) & + TXDCTRL_BUFSZ, PCI_DMA_TODEVICE); + + if (frag != skb_shinfo(skb)->nr_frags) + i++; + } + dev_kfree_skb_any(skb); + } + } +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_init_rings(struct gem *gp) +{ + struct gem_init_block *gb = gp->init_block; + struct net_device *dev = gp->dev; + int i; + dma_addr_t dma_addr; + + gp->rx_new = gp->rx_old = gp->tx_new = gp->tx_old = 0; + + gem_clean_rings(gp); + + gp->rx_buf_sz = max(dev->mtu + ETH_HLEN + VLAN_HLEN, + (unsigned)VLAN_ETH_FRAME_LEN); + + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + struct gem_rxd *rxd = &gb->rxd[i]; + + skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC); + if (!skb) { + rxd->buffer = 0; + rxd->status_word = 0; + continue; + } + + gp->rx_skbs[i] = skb; + skb->dev = dev; + skb_put(skb, (gp->rx_buf_sz + RX_OFFSET)); + dma_addr = pci_map_page(gp->pdev, + virt_to_page(skb->data), + offset_in_page(skb->data), + RX_BUF_ALLOC_SIZE(gp), + PCI_DMA_FROMDEVICE); + rxd->buffer = cpu_to_le64(dma_addr); + wmb(); + rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp)); + skb_reserve(skb, RX_OFFSET); + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct gem_txd *txd = &gb->txd[i]; + + txd->control_word = 0; + wmb(); + txd->buffer = 0; + } + wmb(); +} + +/* Init PHY interface and start link poll state machine */ +static void gem_init_phy(struct gem *gp) +{ + u32 mifcfg; + + /* Revert MIF CFG setting done on stop_phy */ + mifcfg = readl(gp->regs + MIF_CFG); + mifcfg &= ~MIF_CFG_BBMODE; + writel(mifcfg, gp->regs + MIF_CFG); + + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { + int i; + + /* Those delay sucks, the HW seem to love them though, I'll + * serisouly consider breaking some locks here to be able + * to schedule instead + */ + for (i = 0; i < 3; i++) { +#ifdef CONFIG_PPC_PMAC + pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0); + msleep(20); +#endif + /* Some PHYs used by apple have problem getting back to us, + * we do an additional reset here + */ + phy_write(gp, MII_BMCR, BMCR_RESET); + msleep(20); + if (phy_read(gp, MII_BMCR) != 0xffff) + break; + if (i == 2) + printk(KERN_WARNING "%s: GMAC PHY not responding !\n", + gp->dev->name); + } + } + + if (gp->pdev->vendor == PCI_VENDOR_ID_SUN && + gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) { + u32 val; + + /* Init datapath mode register. */ + if (gp->phy_type == phy_mii_mdio0 || + gp->phy_type == phy_mii_mdio1) { + val = PCS_DMODE_MGM; + } else if (gp->phy_type == phy_serialink) { + val = PCS_DMODE_SM | PCS_DMODE_GMOE; + } else { + val = PCS_DMODE_ESM; + } + + writel(val, gp->regs + PCS_DMODE); + } + + if (gp->phy_type == phy_mii_mdio0 || + gp->phy_type == phy_mii_mdio1) { + // XXX check for errors + mii_phy_probe(&gp->phy_mii, gp->mii_phy_addr); + + /* Init PHY */ + if (gp->phy_mii.def && gp->phy_mii.def->ops->init) + gp->phy_mii.def->ops->init(&gp->phy_mii); + } else { + u32 val; + int limit; + + /* Reset PCS unit. */ + val = readl(gp->regs + PCS_MIICTRL); + val |= PCS_MIICTRL_RST; + writeb(val, gp->regs + PCS_MIICTRL); + + limit = 32; + while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) { + udelay(100); + if (limit-- <= 0) + break; + } + if (limit <= 0) + printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", + gp->dev->name); + + /* Make sure PCS is disabled while changing advertisement + * configuration. + */ + val = readl(gp->regs + PCS_CFG); + val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO); + writel(val, gp->regs + PCS_CFG); + + /* Advertise all capabilities except assymetric + * pause. + */ + val = readl(gp->regs + PCS_MIIADV); + val |= (PCS_MIIADV_FD | PCS_MIIADV_HD | + PCS_MIIADV_SP | PCS_MIIADV_AP); + writel(val, gp->regs + PCS_MIIADV); + + /* Enable and restart auto-negotiation, disable wrapback/loopback, + * and re-enable PCS. + */ + val = readl(gp->regs + PCS_MIICTRL); + val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE); + val &= ~PCS_MIICTRL_WB; + writel(val, gp->regs + PCS_MIICTRL); + + val = readl(gp->regs + PCS_CFG); + val |= PCS_CFG_ENABLE; + writel(val, gp->regs + PCS_CFG); + + /* Make sure serialink loopback is off. The meaning + * of this bit is logically inverted based upon whether + * you are in Serialink or SERDES mode. + */ + val = readl(gp->regs + PCS_SCTRL); + if (gp->phy_type == phy_serialink) + val &= ~PCS_SCTRL_LOOP; + else + val |= PCS_SCTRL_LOOP; + writel(val, gp->regs + PCS_SCTRL); + } + + /* Default aneg parameters */ + gp->timer_ticks = 0; + gp->lstate = link_down; + netif_carrier_off(gp->dev); + + /* Can I advertise gigabit here ? I'd need BCM PHY docs... */ + spin_lock_irq(&gp->lock); + gem_begin_auto_negotiation(gp, NULL); + spin_unlock_irq(&gp->lock); +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_init_dma(struct gem *gp) +{ + u64 desc_dma = (u64) gp->gblock_dvma; + u32 val; + + val = (TXDMA_CFG_BASE | (0x7ff << 10) | TXDMA_CFG_PMODE); + writel(val, gp->regs + TXDMA_CFG); + + writel(desc_dma >> 32, gp->regs + TXDMA_DBHI); + writel(desc_dma & 0xffffffff, gp->regs + TXDMA_DBLOW); + desc_dma += (INIT_BLOCK_TX_RING_SIZE * sizeof(struct gem_txd)); + + writel(0, gp->regs + TXDMA_KICK); + + val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) | + ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128); + writel(val, gp->regs + RXDMA_CFG); + + 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 = (((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); + + 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); +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static u32 gem_setup_multicast(struct gem *gp) +{ + u32 rxcfg = 0; + int i; + + if ((gp->dev->flags & IFF_ALLMULTI) || + (gp->dev->mc_count > 256)) { + for (i=0; i<16; i++) + writel(0xffff, gp->regs + MAC_HASH0 + (i << 2)); + rxcfg |= MAC_RXCFG_HFE; + } else if (gp->dev->flags & IFF_PROMISC) { + rxcfg |= MAC_RXCFG_PROM; + } else { + u16 hash_table[16]; + u32 crc; + struct dev_mc_list *dmi = gp->dev->mc_list; + int i; + + for (i = 0; i < 16; i++) + hash_table[i] = 0; + + for (i = 0; i < gp->dev->mc_count; i++) { + char *addrs = dmi->dmi_addr; + + dmi = dmi->next; + + if (!(*addrs & 1)) + continue; + + crc = ether_crc_le(6, addrs); + crc >>= 24; + hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf)); + } + for (i=0; i<16; i++) + writel(hash_table[i], gp->regs + MAC_HASH0 + (i << 2)); + rxcfg |= MAC_RXCFG_HFE; + } + + return rxcfg; +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_init_mac(struct gem *gp) +{ + unsigned char *e = &gp->dev->dev_addr[0]; + + writel(0x1bf0, gp->regs + MAC_SNDPAUSE); + + writel(0x00, gp->regs + MAC_IPG0); + writel(0x08, gp->regs + MAC_IPG1); + writel(0x04, gp->regs + MAC_IPG2); + writel(0x40, gp->regs + MAC_STIME); + writel(0x40, gp->regs + MAC_MINFSZ); + + /* Ethernet payload + header + FCS + optional VLAN tag. */ + writel(0x20000000 | (gp->rx_buf_sz + 4), gp->regs + MAC_MAXFSZ); + + writel(0x07, gp->regs + MAC_PASIZE); + writel(0x04, gp->regs + MAC_JAMSIZE); + writel(0x10, gp->regs + MAC_ATTLIM); + writel(0x8808, gp->regs + MAC_MCTYPE); + + writel((e[5] | (e[4] << 8)) & 0x3ff, gp->regs + MAC_RANDSEED); + + writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0); + writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1); + writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2); + + writel(0, gp->regs + MAC_ADDR3); + writel(0, gp->regs + MAC_ADDR4); + writel(0, gp->regs + MAC_ADDR5); + + writel(0x0001, gp->regs + MAC_ADDR6); + writel(0xc200, gp->regs + MAC_ADDR7); + writel(0x0180, gp->regs + MAC_ADDR8); + + writel(0, gp->regs + MAC_AFILT0); + writel(0, gp->regs + MAC_AFILT1); + writel(0, gp->regs + MAC_AFILT2); + writel(0, gp->regs + MAC_AF21MSK); + writel(0, gp->regs + MAC_AF0MSK); + + gp->mac_rx_cfg = gem_setup_multicast(gp); +#ifdef STRIP_FCS + gp->mac_rx_cfg |= MAC_RXCFG_SFCS; +#endif + writel(0, gp->regs + MAC_NCOLL); + writel(0, gp->regs + MAC_FASUCC); + writel(0, gp->regs + MAC_ECOLL); + writel(0, gp->regs + MAC_LCOLL); + writel(0, gp->regs + MAC_DTIMER); + writel(0, gp->regs + MAC_PATMPS); + writel(0, gp->regs + MAC_RFCTR); + writel(0, gp->regs + MAC_LERR); + writel(0, gp->regs + MAC_AERR); + writel(0, gp->regs + MAC_FCSERR); + writel(0, gp->regs + MAC_RXCVERR); + + /* Clear RX/TX/MAC/XIF config, we will set these up and enable + * them once a link is established. + */ + writel(0, gp->regs + MAC_TXCFG); + writel(gp->mac_rx_cfg, gp->regs + MAC_RXCFG); + writel(0, gp->regs + MAC_MCCFG); + writel(0, gp->regs + MAC_XIFCFG); + + /* Setup MAC interrupts. We want to get all of the interesting + * counter expiration events, but we do not want to hear about + * normal rx/tx as the DMA engine tells us that. + */ + writel(MAC_TXSTAT_XMIT, gp->regs + MAC_TXMASK); + writel(MAC_RXSTAT_RCV, gp->regs + MAC_RXMASK); + + /* Don't enable even the PAUSE interrupts for now, we + * make no use of those events other than to record them. + */ + writel(0xffffffff, gp->regs + MAC_MCMASK); + + /* Don't enable GEM's WOL in normal operations + */ + if (gp->has_wol) + writel(0, gp->regs + WOL_WAKECSR); +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_init_pause_thresholds(struct gem *gp) +{ + u32 cfg; + + /* Calculate pause thresholds. Setting the OFF threshold to the + * full RX fifo size effectively disables PAUSE generation which + * is what we do for 10/100 only GEMs which have FIFOs too small + * to make real gains from PAUSE. + */ + if (gp->rx_fifo_sz <= (2 * 1024)) { + gp->rx_pause_off = gp->rx_pause_on = gp->rx_fifo_sz; + } else { + int max_frame = (gp->rx_buf_sz + 4 + 64) & ~63; + int off = (gp->rx_fifo_sz - (max_frame * 2)); + int on = off - max_frame; + + gp->rx_pause_off = off; + gp->rx_pause_on = on; + } + + + /* Configure the chip "burst" DMA mode & enable some + * HW bug fixes on Apple version + */ + cfg = 0; + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) + cfg |= GREG_CFG_RONPAULBIT | GREG_CFG_ENBUG2FIX; +#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA) + cfg |= GREG_CFG_IBURST; +#endif + cfg |= ((31 << 1) & GREG_CFG_TXDMALIM); + cfg |= ((31 << 6) & GREG_CFG_RXDMALIM); + writel(cfg, gp->regs + GREG_CFG); + + /* If Infinite Burst didn't stick, then use different + * thresholds (and Apple bug fixes don't exist) + */ + if (!(readl(gp->regs + GREG_CFG) & GREG_CFG_IBURST)) { + cfg = ((2 << 1) & GREG_CFG_TXDMALIM); + cfg |= ((8 << 6) & GREG_CFG_RXDMALIM); + writel(cfg, gp->regs + GREG_CFG); + } +} + +static int gem_check_invariants(struct gem *gp) +{ + struct pci_dev *pdev = gp->pdev; + u32 mif_cfg; + + /* On Apple's sungem, we can't rely on registers as the chip + * was been powered down by the firmware. The PHY is looked + * up later on. + */ + if (pdev->vendor == PCI_VENDOR_ID_APPLE) { + gp->phy_type = phy_mii_mdio0; + gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64; + gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64; + gp->swrst_base = 0; + + mif_cfg = readl(gp->regs + MIF_CFG); + mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1); + mif_cfg |= MIF_CFG_MDI0; + writel(mif_cfg, gp->regs + MIF_CFG); + writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE); + writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG); + + /* We hard-code the PHY address so we can properly bring it out of + * reset later on, we can't really probe it at this point, though + * that isn't an issue. + */ + if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC) + gp->mii_phy_addr = 1; + else + gp->mii_phy_addr = 0; + + return 0; + } + + mif_cfg = readl(gp->regs + MIF_CFG); + + if (pdev->vendor == PCI_VENDOR_ID_SUN && + pdev->device == PCI_DEVICE_ID_SUN_RIO_GEM) { + /* One of the MII PHYs _must_ be present + * as this chip has no gigabit PHY. + */ + if ((mif_cfg & (MIF_CFG_MDI0 | MIF_CFG_MDI1)) == 0) { + printk(KERN_ERR PFX "RIO GEM lacks MII phy, mif_cfg[%08x]\n", + mif_cfg); + return -1; + } + } + + /* Determine initial PHY interface type guess. MDIO1 is the + * external PHY and thus takes precedence over MDIO0. + */ + + if (mif_cfg & MIF_CFG_MDI1) { + gp->phy_type = phy_mii_mdio1; + mif_cfg |= MIF_CFG_PSELECT; + writel(mif_cfg, gp->regs + MIF_CFG); + } else if (mif_cfg & MIF_CFG_MDI0) { + gp->phy_type = phy_mii_mdio0; + mif_cfg &= ~MIF_CFG_PSELECT; + writel(mif_cfg, gp->regs + MIF_CFG); + } else { + gp->phy_type = phy_serialink; + } + if (gp->phy_type == phy_mii_mdio1 || + gp->phy_type == phy_mii_mdio0) { + int i; + + for (i = 0; i < 32; i++) { + gp->mii_phy_addr = i; + if (phy_read(gp, MII_BMCR) != 0xffff) + break; + } + if (i == 32) { + if (pdev->device != PCI_DEVICE_ID_SUN_GEM) { + printk(KERN_ERR PFX "RIO MII phy will not respond.\n"); + return -1; + } + gp->phy_type = phy_serdes; + } + } + + /* Fetch the FIFO configurations now too. */ + gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64; + gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64; + + if (pdev->vendor == PCI_VENDOR_ID_SUN) { + if (pdev->device == PCI_DEVICE_ID_SUN_GEM) { + if (gp->tx_fifo_sz != (9 * 1024) || + gp->rx_fifo_sz != (20 * 1024)) { + printk(KERN_ERR PFX "GEM has bogus fifo sizes tx(%d) rx(%d)\n", + gp->tx_fifo_sz, gp->rx_fifo_sz); + return -1; + } + gp->swrst_base = 0; + } else { + if (gp->tx_fifo_sz != (2 * 1024) || + gp->rx_fifo_sz != (2 * 1024)) { + printk(KERN_ERR PFX "RIO GEM has bogus fifo sizes tx(%d) rx(%d)\n", + gp->tx_fifo_sz, gp->rx_fifo_sz); + return -1; + } + gp->swrst_base = (64 / 4) << GREG_SWRST_CACHE_SHIFT; + } + } + + return 0; +} + +/* Must be invoked under gp->lock and gp->tx_lock. */ +static void gem_reinit_chip(struct gem *gp) +{ + /* Reset the chip */ + gem_reset(gp); + + /* Make sure ints are disabled */ + gem_disable_ints(gp); + + /* Allocate & setup ring buffers */ + gem_init_rings(gp); + + /* Configure pause thresholds */ + gem_init_pause_thresholds(gp); + + /* Init DMA & MAC engines */ + gem_init_dma(gp); + gem_init_mac(gp); +} + + +/* Must be invoked with no lock held. */ +static void gem_stop_phy(struct gem *gp, int wol) +{ + u32 mifcfg; + unsigned long flags; + + /* Let the chip settle down a bit, it seems that helps + * for sleep mode on some models + */ + msleep(10); + + /* Make sure we aren't polling PHY status change. We + * don't currently use that feature though + */ + mifcfg = readl(gp->regs + MIF_CFG); + mifcfg &= ~MIF_CFG_POLL; + writel(mifcfg, gp->regs + MIF_CFG); + + if (wol && gp->has_wol) { + unsigned char *e = &gp->dev->dev_addr[0]; + u32 csr; + + /* Setup wake-on-lan for MAGIC packet */ + writel(MAC_RXCFG_HFE | MAC_RXCFG_SFCS | MAC_RXCFG_ENAB, + gp->regs + MAC_RXCFG); + writel((e[4] << 8) | e[5], gp->regs + WOL_MATCH0); + writel((e[2] << 8) | e[3], gp->regs + WOL_MATCH1); + writel((e[0] << 8) | e[1], gp->regs + WOL_MATCH2); + + writel(WOL_MCOUNT_N | WOL_MCOUNT_M, gp->regs + WOL_MCOUNT); + csr = WOL_WAKECSR_ENABLE; + if ((readl(gp->regs + MAC_XIFCFG) & MAC_XIFCFG_GMII) == 0) + csr |= WOL_WAKECSR_MII; + writel(csr, gp->regs + WOL_WAKECSR); + } else { + writel(0, gp->regs + MAC_RXCFG); + (void)readl(gp->regs + MAC_RXCFG); + /* Machine sleep will die in strange ways if we + * dont wait a bit here, looks like the chip takes + * some time to really shut down + */ + msleep(10); + } + + writel(0, gp->regs + MAC_TXCFG); + writel(0, gp->regs + MAC_XIFCFG); + writel(0, gp->regs + TXDMA_CFG); + writel(0, gp->regs + RXDMA_CFG); + + if (!wol) { + spin_lock_irqsave(&gp->lock, flags); + spin_lock(&gp->tx_lock); + gem_reset(gp); + writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST); + writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST); + spin_unlock(&gp->tx_lock); + spin_unlock_irqrestore(&gp->lock, flags); + + /* No need to take the lock here */ + + if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend) + gp->phy_mii.def->ops->suspend(&gp->phy_mii); + + /* According to Apple, we must set the MDIO pins to this begnign + * state or we may 1) eat more current, 2) damage some PHYs + */ + writel(mifcfg | MIF_CFG_BBMODE, gp->regs + MIF_CFG); + writel(0, gp->regs + MIF_BBCLK); + writel(0, gp->regs + MIF_BBDATA); + writel(0, gp->regs + MIF_BBOENAB); + writel(MAC_XIFCFG_GMII | MAC_XIFCFG_LBCK, gp->regs + MAC_XIFCFG); + (void) readl(gp->regs + MAC_XIFCFG); + } +} + + +static int gem_do_start(struct net_device *dev) +{ + struct gem *gp = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&gp->lock, flags); + spin_lock(&gp->tx_lock); + + /* Enable the cell */ + gem_get_cell(gp); + + /* Init & setup chip hardware */ + gem_reinit_chip(gp); + + gp->running = 1; + + if (gp->lstate == link_up) { + netif_carrier_on(gp->dev); + gem_set_link_modes(gp); + } + + netif_wake_queue(gp->dev); + + spin_unlock(&gp->tx_lock); + spin_unlock_irqrestore(&gp->lock, flags); + + if (request_irq(gp->pdev->irq, gem_interrupt, + SA_SHIRQ, dev->name, (void *)dev)) { + printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name); + + spin_lock_irqsave(&gp->lock, flags); + spin_lock(&gp->tx_lock); + + gp->running = 0; + gem_reset(gp); + gem_clean_rings(gp); + gem_put_cell(gp); + + spin_unlock(&gp->tx_lock); + spin_unlock_irqrestore(&gp->lock, flags); + + return -EAGAIN; + } + + return 0; +} + +static void gem_do_stop(struct net_device *dev, int wol) +{ + struct gem *gp = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&gp->lock, flags); + spin_lock(&gp->tx_lock); + + gp->running = 0; + + /* Stop netif queue */ + netif_stop_queue(dev); + + /* Make sure ints are disabled */ + gem_disable_ints(gp); + + /* We can drop the lock now */ + spin_unlock(&gp->tx_lock); + spin_unlock_irqrestore(&gp->lock, flags); + + /* If we are going to sleep with WOL */ + gem_stop_dma(gp); + msleep(10); + if (!wol) + gem_reset(gp); + msleep(10); + + /* Get rid of rings */ + gem_clean_rings(gp); + + /* No irq needed anymore */ + free_irq(gp->pdev->irq, (void *) dev); + + /* Cell not needed neither if no WOL */ + if (!wol) { + spin_lock_irqsave(&gp->lock, flags); + gem_put_cell(gp); + spin_unlock_irqrestore(&gp->lock, flags); + } +} + +static void gem_reset_task(void *data) +{ + struct gem *gp = (struct gem *) data; + + down(&gp->pm_sem); + + netif_poll_disable(gp->dev); + + spin_lock_irq(&gp->lock); + spin_lock(&gp->tx_lock); + + if (gp->running == 0) + goto not_running; + + if (gp->running) { + netif_stop_queue(gp->dev); + + /* Reset the chip & rings */ + gem_reinit_chip(gp); + if (gp->lstate == link_up) + gem_set_link_modes(gp); + netif_wake_queue(gp->dev); + } + not_running: + gp->reset_task_pending = 0; + + spin_unlock(&gp->tx_lock); + spin_unlock_irq(&gp->lock); + + netif_poll_enable(gp->dev); + + up(&gp->pm_sem); +} + + +static int gem_open(struct net_device *dev) +{ + struct gem *gp = dev->priv; + int rc = 0; + + down(&gp->pm_sem); + + /* We need the cell enabled */ + if (!gp->asleep) + rc = gem_do_start(dev); + gp->opened = (rc == 0); + + up(&gp->pm_sem); + + return rc; +} + +static int gem_close(struct net_device *dev) +{ + struct gem *gp = dev->priv; + + /* Note: we don't need to call netif_poll_disable() here because + * our caller (dev_close) already did it for us + */ + + down(&gp->pm_sem); + + gp->opened = 0; + if (!gp->asleep) + gem_do_stop(dev, 0); + + up(&gp->pm_sem); + + return 0; +} + +#ifdef CONFIG_PM +static int gem_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct gem *gp = dev->priv; + unsigned long flags; + + down(&gp->pm_sem); + + netif_poll_disable(dev); + + printk(KERN_INFO "%s: suspending, WakeOnLan %s\n", + dev->name, + (gp->wake_on_lan && gp->opened) ? "enabled" : "disabled"); + + /* Keep the cell enabled during the entire operation */ + spin_lock_irqsave(&gp->lock, flags); + spin_lock(&gp->tx_lock); + gem_get_cell(gp); + spin_unlock(&gp->tx_lock); + spin_unlock_irqrestore(&gp->lock, flags); + + /* If the driver is opened, we stop the MAC */ + if (gp->opened) { + /* Stop traffic, mark us closed */ + netif_device_detach(dev); + + /* Switch off MAC, remember WOL setting */ + gp->asleep_wol = gp->wake_on_lan; + gem_do_stop(dev, gp->asleep_wol); + } else + gp->asleep_wol = 0; + + /* Mark us asleep */ + gp->asleep = 1; + wmb(); + + /* Stop the link timer */ + del_timer_sync(&gp->link_timer); + + /* Now we release the semaphore to not block the reset task who + * can take it too. We are marked asleep, so there will be no + * conflict here + */ + up(&gp->pm_sem); + + /* Wait for a pending reset task to complete */ + while (gp->reset_task_pending) + yield(); + flush_scheduled_work(); + + /* Shut the PHY down eventually and setup WOL */ + gem_stop_phy(gp, gp->asleep_wol); + + /* Make sure bus master is disabled */ + pci_disable_device(gp->pdev); + + /* Release the cell, no need to take a lock at this point since + * nothing else can happen now + */ + gem_put_cell(gp); + + return 0; +} + +static int gem_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct gem *gp = dev->priv; + unsigned long flags; + + printk(KERN_INFO "%s: resuming\n", dev->name); + + down(&gp->pm_sem); + + /* Keep the cell enabled during the entire operation, no need to + * take a lock here tho since nothing else can happen while we are + * marked asleep + */ + gem_get_cell(gp); + + /* Make sure PCI access and bus master are enabled */ + if (pci_enable_device(gp->pdev)) { + printk(KERN_ERR "%s: Can't re-enable chip !\n", + dev->name); + /* Put cell and forget it for now, it will be considered as + * still asleep, a new sleep cycle may bring it back + */ + gem_put_cell(gp); + up(&gp->pm_sem); + return 0; + } + pci_set_master(gp->pdev); + + /* Reset everything */ + gem_reset(gp); + + /* Mark us woken up */ + gp->asleep = 0; + wmb(); + + /* Bring the PHY back. Again, lock is useless at this point as + * nothing can be happening until we restart the whole thing + */ + gem_init_phy(gp); + + /* If we were opened, bring everything back */ + if (gp->opened) { + /* Restart MAC */ + gem_do_start(dev); + + /* Re-attach net device */ + netif_device_attach(dev); + + } + + spin_lock_irqsave(&gp->lock, flags); + spin_lock(&gp->tx_lock); + + /* If we had WOL enabled, the cell clock was never turned off during + * sleep, so we end up beeing unbalanced. Fix that here + */ + if (gp->asleep_wol) + gem_put_cell(gp); + + /* This function doesn't need to hold the cell, it will be held if the + * driver is open by gem_do_start(). + */ + gem_put_cell(gp); + + spin_unlock(&gp->tx_lock); + spin_unlock_irqrestore(&gp->lock, flags); + + netif_poll_enable(dev); + + up(&gp->pm_sem); + + return 0; +} +#endif /* CONFIG_PM */ + +static struct net_device_stats *gem_get_stats(struct net_device *dev) +{ + struct gem *gp = dev->priv; + struct net_device_stats *stats = &gp->net_stats; + + spin_lock_irq(&gp->lock); + spin_lock(&gp->tx_lock); + + /* I have seen this being called while the PM was in progress, + * so we shield against this + */ + if (gp->running) { + stats->rx_crc_errors += readl(gp->regs + MAC_FCSERR); + writel(0, gp->regs + MAC_FCSERR); + + stats->rx_frame_errors += readl(gp->regs + MAC_AERR); + writel(0, gp->regs + MAC_AERR); + + stats->rx_length_errors += readl(gp->regs + MAC_LERR); + writel(0, gp->regs + MAC_LERR); + + stats->tx_aborted_errors += readl(gp->regs + MAC_ECOLL); + stats->collisions += + (readl(gp->regs + MAC_ECOLL) + + readl(gp->regs + MAC_LCOLL)); + writel(0, gp->regs + MAC_ECOLL); + writel(0, gp->regs + MAC_LCOLL); + } + + spin_unlock(&gp->tx_lock); + spin_unlock_irq(&gp->lock); + + return &gp->net_stats; +} + +static void gem_set_multicast(struct net_device *dev) +{ + struct gem *gp = dev->priv; + u32 rxcfg, rxcfg_new; + int limit = 10000; + + + spin_lock_irq(&gp->lock); + spin_lock(&gp->tx_lock); + + if (!gp->running) + goto bail; + + netif_stop_queue(dev); + + rxcfg = readl(gp->regs + MAC_RXCFG); + rxcfg_new = gem_setup_multicast(gp); +#ifdef STRIP_FCS + rxcfg_new |= MAC_RXCFG_SFCS; +#endif + gp->mac_rx_cfg = rxcfg_new; + + writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG); + while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) { + if (!limit--) + break; + udelay(10); + } + + rxcfg &= ~(MAC_RXCFG_PROM | MAC_RXCFG_HFE); + rxcfg |= rxcfg_new; + + writel(rxcfg, gp->regs + MAC_RXCFG); + + netif_wake_queue(dev); + + bail: + spin_unlock(&gp->tx_lock); + spin_unlock_irq(&gp->lock); +} + +/* Jumbo-grams don't seem to work :-( */ +#define GEM_MIN_MTU 68 +#if 1 +#define GEM_MAX_MTU 1500 +#else +#define GEM_MAX_MTU 9000 +#endif + +static int gem_change_mtu(struct net_device *dev, int new_mtu) +{ + struct gem *gp = dev->priv; + + if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU) + return -EINVAL; + + if (!netif_running(dev) || !netif_device_present(dev)) { + /* We'll just catch it later when the + * device is up'd or resumed. + */ + dev->mtu = new_mtu; + return 0; + } + + down(&gp->pm_sem); + spin_lock_irq(&gp->lock); + spin_lock(&gp->tx_lock); + dev->mtu = new_mtu; + if (gp->running) { + gem_reinit_chip(gp); + if (gp->lstate == link_up) + gem_set_link_modes(gp); + } + spin_unlock(&gp->tx_lock); + spin_unlock_irq(&gp->lock); + up(&gp->pm_sem); + + return 0; +} + +static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct gem *gp = dev->priv; + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(gp->pdev)); +} + +static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct gem *gp = dev->priv; + + if (gp->phy_type == phy_mii_mdio0 || + gp->phy_type == phy_mii_mdio1) { + if (gp->phy_mii.def) + cmd->supported = gp->phy_mii.def->features; + else + cmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full); + + /* XXX hardcoded stuff for now */ + cmd->port = PORT_MII; + cmd->transceiver = XCVR_EXTERNAL; + cmd->phy_address = 0; /* XXX fixed PHYAD */ + + /* Return current PHY settings */ + spin_lock_irq(&gp->lock); + cmd->autoneg = gp->want_autoneg; + cmd->speed = gp->phy_mii.speed; + cmd->duplex = gp->phy_mii.duplex; + cmd->advertising = gp->phy_mii.advertising; + + /* If we started with a forced mode, we don't have a default + * advertise set, we need to return something sensible so + * userland can re-enable autoneg properly. + */ + if (cmd->advertising == 0) + cmd->advertising = cmd->supported; + spin_unlock_irq(&gp->lock); + } else { // XXX PCS ? + cmd->supported = + (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg); + cmd->advertising = cmd->supported; + cmd->speed = 0; + cmd->duplex = cmd->port = cmd->phy_address = + cmd->transceiver = cmd->autoneg = 0; + } + cmd->maxtxpkt = cmd->maxrxpkt = 0; + + return 0; +} + +static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct gem *gp = dev->priv; + + /* Verify the settings we care about. */ + if (cmd->autoneg != AUTONEG_ENABLE && + cmd->autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (cmd->autoneg == AUTONEG_ENABLE && + cmd->advertising == 0) + return -EINVAL; + + if (cmd->autoneg == AUTONEG_DISABLE && + ((cmd->speed != SPEED_1000 && + cmd->speed != SPEED_100 && + cmd->speed != SPEED_10) || + (cmd->duplex != DUPLEX_HALF && + cmd->duplex != DUPLEX_FULL))) + return -EINVAL; + + /* Apply settings and restart link process. */ + spin_lock_irq(&gp->lock); + gem_get_cell(gp); + gem_begin_auto_negotiation(gp, cmd); + gem_put_cell(gp); + spin_unlock_irq(&gp->lock); + + return 0; +} + +static int gem_nway_reset(struct net_device *dev) +{ + struct gem *gp = dev->priv; + + if (!gp->want_autoneg) + return -EINVAL; + + /* Restart link process. */ + spin_lock_irq(&gp->lock); + gem_get_cell(gp); + gem_begin_auto_negotiation(gp, NULL); + gem_put_cell(gp); + spin_unlock_irq(&gp->lock); + + return 0; +} + +static u32 gem_get_msglevel(struct net_device *dev) +{ + struct gem *gp = dev->priv; + return gp->msg_enable; +} + +static void gem_set_msglevel(struct net_device *dev, u32 value) +{ + struct gem *gp = dev->priv; + gp->msg_enable = value; +} + + +/* Add more when I understand how to program the chip */ +/* like WAKE_UCAST | WAKE_MCAST | WAKE_BCAST */ + +#define WOL_SUPPORTED_MASK (WAKE_MAGIC) + +static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gem *gp = dev->priv; + + /* Add more when I understand how to program the chip */ + if (gp->has_wol) { + wol->supported = WOL_SUPPORTED_MASK; + wol->wolopts = gp->wake_on_lan; + } else { + wol->supported = 0; + wol->wolopts = 0; + } +} + +static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gem *gp = dev->priv; + + if (!gp->has_wol) + return -EOPNOTSUPP; + gp->wake_on_lan = wol->wolopts & WOL_SUPPORTED_MASK; + return 0; +} + +static struct ethtool_ops gem_ethtool_ops = { + .get_drvinfo = gem_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_settings = gem_get_settings, + .set_settings = gem_set_settings, + .nway_reset = gem_nway_reset, + .get_msglevel = gem_get_msglevel, + .set_msglevel = gem_set_msglevel, + .get_wol = gem_get_wol, + .set_wol = gem_set_wol, +}; + +static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct gem *gp = dev->priv; + struct mii_ioctl_data *data = if_mii(ifr); + int rc = -EOPNOTSUPP; + unsigned long flags; + + /* Hold the PM semaphore while doing ioctl's or we may collide + * with power management. + */ + down(&gp->pm_sem); + + spin_lock_irqsave(&gp->lock, flags); + gem_get_cell(gp); + spin_unlock_irqrestore(&gp->lock, flags); + + switch (cmd) { + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + data->phy_id = gp->mii_phy_addr; + /* Fallthrough... */ + + case SIOCGMIIREG: /* Read MII PHY register. */ + if (!gp->running) + rc = -EAGAIN; + else { + data->val_out = __phy_read(gp, data->phy_id & 0x1f, + data->reg_num & 0x1f); + rc = 0; + } + break; + + case SIOCSMIIREG: /* Write MII PHY register. */ + if (!capable(CAP_NET_ADMIN)) + rc = -EPERM; + else if (!gp->running) + rc = -EAGAIN; + else { + __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f, + data->val_in); + rc = 0; + } + break; + }; + + spin_lock_irqsave(&gp->lock, flags); + gem_put_cell(gp); + spin_unlock_irqrestore(&gp->lock, flags); + + up(&gp->pm_sem); + + return rc; +} + +#if (!defined(__sparc__) && !defined(CONFIG_PPC_PMAC)) +/* Fetch MAC address from vital product data of PCI ROM. */ +static void find_eth_addr_in_vpd(void __iomem *rom_base, int len, unsigned char *dev_addr) +{ + int this_offset; + + for (this_offset = 0x20; this_offset < len; this_offset++) { + void __iomem *p = rom_base + this_offset; + int i; + + if (readb(p + 0) != 0x90 || + readb(p + 1) != 0x00 || + readb(p + 2) != 0x09 || + readb(p + 3) != 0x4e || + readb(p + 4) != 0x41 || + readb(p + 5) != 0x06) + continue; + + this_offset += 6; + p += 6; + + for (i = 0; i < 6; i++) + dev_addr[i] = readb(p + i); + break; + } +} + +static void get_gem_mac_nonobp(struct pci_dev *pdev, unsigned char *dev_addr) +{ + u32 rom_reg_orig; + void __iomem *p; + + if (pdev->resource[PCI_ROM_RESOURCE].parent == NULL) { + if (pci_assign_resource(pdev, PCI_ROM_RESOURCE) < 0) + goto use_random; + } + + pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_reg_orig); + pci_write_config_dword(pdev, pdev->rom_base_reg, + rom_reg_orig | PCI_ROM_ADDRESS_ENABLE); + + p = ioremap(pci_resource_start(pdev, PCI_ROM_RESOURCE), (64 * 1024)); + if (p != NULL && readb(p) == 0x55 && readb(p + 1) == 0xaa) + find_eth_addr_in_vpd(p, (64 * 1024), dev_addr); + + if (p != NULL) + iounmap(p); + + pci_write_config_dword(pdev, pdev->rom_base_reg, rom_reg_orig); + return; + +use_random: + /* Sun MAC prefix then 3 random bytes. */ + dev_addr[0] = 0x08; + dev_addr[1] = 0x00; + dev_addr[2] = 0x20; + get_random_bytes(dev_addr + 3, 3); + return; +} +#endif /* not Sparc and not PPC */ + +static int __devinit gem_get_device_address(struct gem *gp) +{ +#if defined(__sparc__) || defined(CONFIG_PPC_PMAC) + struct net_device *dev = gp->dev; +#endif + +#if defined(__sparc__) + struct pci_dev *pdev = gp->pdev; + struct pcidev_cookie *pcp = pdev->sysdata; + int node = -1; + + if (pcp != NULL) { + node = pcp->prom_node; + if (prom_getproplen(node, "local-mac-address") == 6) + prom_getproperty(node, "local-mac-address", + dev->dev_addr, 6); + else + node = -1; + } + if (node == -1) + memcpy(dev->dev_addr, idprom->id_ethaddr, 6); +#elif defined(CONFIG_PPC_PMAC) + unsigned char *addr; + + addr = get_property(gp->of_node, "local-mac-address", NULL); + if (addr == NULL) { + printk("\n"); + printk(KERN_ERR "%s: can't get mac-address\n", dev->name); + return -1; + } + memcpy(dev->dev_addr, addr, 6); +#else + get_gem_mac_nonobp(gp->pdev, gp->dev->dev_addr); +#endif + return 0; +} + +static void __devexit gem_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + struct gem *gp = dev->priv; + + unregister_netdev(dev); + + /* Stop the link timer */ + del_timer_sync(&gp->link_timer); + + /* We shouldn't need any locking here */ + gem_get_cell(gp); + + /* Wait for a pending reset task to complete */ + while (gp->reset_task_pending) + yield(); + flush_scheduled_work(); + + /* Shut the PHY down */ + gem_stop_phy(gp, 0); + + gem_put_cell(gp); + + /* Make sure bus master is disabled */ + pci_disable_device(gp->pdev); + + /* Free resources */ + pci_free_consistent(pdev, + sizeof(struct gem_init_block), + gp->init_block, + gp->gblock_dvma); + iounmap(gp->regs); + pci_release_regions(pdev); + free_netdev(dev); + + pci_set_drvdata(pdev, NULL); + } +} + +static int __devinit gem_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static int gem_version_printed = 0; + unsigned long gemreg_base, gemreg_len; + struct net_device *dev; + struct gem *gp; + int i, err, pci_using_dac; + + if (gem_version_printed++ == 0) + printk(KERN_INFO "%s", version); + + /* Apple gmac note: during probe, the chip is powered up by + * the arch code to allow the code below to work (and to let + * the chip be probed on the config space. It won't stay powered + * up until the interface is brought up however, so we can't rely + * on register configuration done at this point. + */ + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable MMIO operation, " + "aborting.\n"); + return err; + } + pci_set_master(pdev); + + /* Configure DMA attributes. */ + + /* All of the GEM documentation states that 64-bit DMA addressing + * is fully supported and should work just fine. However the + * front end for RIO based GEMs is different and only supports + * 32-bit addressing. + * + * For now we assume the various PPC GEMs are 32-bit only as well. + */ + if (pdev->vendor == PCI_VENDOR_ID_SUN && + pdev->device == PCI_DEVICE_ID_SUN_GEM && + !pci_set_dma_mask(pdev, (u64) 0xffffffffffffffffULL)) { + pci_using_dac = 1; + } else { + err = pci_set_dma_mask(pdev, (u64) 0xffffffff); + if (err) { + printk(KERN_ERR PFX "No usable DMA configuration, " + "aborting.\n"); + goto err_disable_device; + } + pci_using_dac = 0; + } + + gemreg_base = pci_resource_start(pdev, 0); + gemreg_len = pci_resource_len(pdev, 0); + + if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0) { + printk(KERN_ERR PFX "Cannot find proper PCI device " + "base address, aborting.\n"); + err = -ENODEV; + goto err_disable_device; + } + + dev = alloc_etherdev(sizeof(*gp)); + if (!dev) { + printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + err = -ENOMEM; + goto err_disable_device; + } + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + gp = dev->priv; + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources, " + "aborting.\n"); + goto err_out_free_netdev; + } + + gp->pdev = pdev; + dev->base_addr = (long) pdev; + gp->dev = dev; + + gp->msg_enable = DEFAULT_MSG; + + spin_lock_init(&gp->lock); + spin_lock_init(&gp->tx_lock); + init_MUTEX(&gp->pm_sem); + + init_timer(&gp->link_timer); + gp->link_timer.function = gem_link_timer; + gp->link_timer.data = (unsigned long) gp; + + INIT_WORK(&gp->reset_task, gem_reset_task, gp); + + gp->lstate = link_down; + gp->timer_ticks = 0; + netif_carrier_off(dev); + + gp->regs = ioremap(gemreg_base, gemreg_len); + if (gp->regs == 0UL) { + printk(KERN_ERR PFX "Cannot map device registers, " + "aborting.\n"); + err = -EIO; + goto err_out_free_res; + } + + /* On Apple, we want a reference to the Open Firmware device-tree + * node. We use it for clock control. + */ +#ifdef CONFIG_PPC_PMAC + gp->of_node = pci_device_to_OF_node(pdev); +#endif + + /* Only Apple version supports WOL afaik */ + if (pdev->vendor == PCI_VENDOR_ID_APPLE) + gp->has_wol = 1; + + /* Make sure cell is enabled */ + gem_get_cell(gp); + + /* Make sure everything is stopped and in init state */ + gem_reset(gp); + + /* Fill up the mii_phy structure (even if we won't use it) */ + gp->phy_mii.dev = dev; + gp->phy_mii.mdio_read = _phy_read; + gp->phy_mii.mdio_write = _phy_write; + + /* By default, we start with autoneg */ + gp->want_autoneg = 1; + + /* Check fifo sizes, PHY type, etc... */ + if (gem_check_invariants(gp)) { + err = -ENODEV; + goto err_out_iounmap; + } + + /* It is guaranteed that the returned buffer will be at least + * PAGE_SIZE aligned. + */ + gp->init_block = (struct gem_init_block *) + pci_alloc_consistent(pdev, sizeof(struct gem_init_block), + &gp->gblock_dvma); + if (!gp->init_block) { + printk(KERN_ERR PFX "Cannot allocate init block, " + "aborting.\n"); + err = -ENOMEM; + goto err_out_iounmap; + } + + if (gem_get_device_address(gp)) + goto err_out_free_consistent; + + dev->open = gem_open; + dev->stop = gem_close; + dev->hard_start_xmit = gem_start_xmit; + dev->get_stats = gem_get_stats; + dev->set_multicast_list = gem_set_multicast; + dev->do_ioctl = gem_ioctl; + dev->poll = gem_poll; + dev->weight = 64; + dev->ethtool_ops = &gem_ethtool_ops; + dev->tx_timeout = gem_tx_timeout; + dev->watchdog_timeo = 5 * HZ; + dev->change_mtu = gem_change_mtu; + dev->irq = pdev->irq; + dev->dma = 0; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = gem_poll_controller; +#endif + + /* Set that now, in case PM kicks in now */ + pci_set_drvdata(pdev, dev); + + /* Detect & init PHY, start autoneg, we release the cell now + * too, it will be managed by whoever needs it + */ + gem_init_phy(gp); + + spin_lock_irq(&gp->lock); + gem_put_cell(gp); + spin_unlock_irq(&gp->lock); + + /* Register with kernel */ + if (register_netdev(dev)) { + printk(KERN_ERR PFX "Cannot register net device, " + "aborting.\n"); + err = -ENOMEM; + goto err_out_free_consistent; + } + + printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet ", + dev->name); + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], + i == 5 ? ' ' : ':'); + printk("\n"); + + if (gp->phy_type == phy_mii_mdio0 || + gp->phy_type == phy_mii_mdio1) + printk(KERN_INFO "%s: Found %s PHY\n", dev->name, + gp->phy_mii.def ? gp->phy_mii.def->name : "no"); + + /* GEM can do it all... */ + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_LLTX; + if (pci_using_dac) + dev->features |= NETIF_F_HIGHDMA; + + return 0; + +err_out_free_consistent: + gem_remove_one(pdev); +err_out_iounmap: + gem_put_cell(gp); + iounmap(gp->regs); + +err_out_free_res: + pci_release_regions(pdev); + +err_out_free_netdev: + free_netdev(dev); +err_disable_device: + pci_disable_device(pdev); + return err; + +} + + +static struct pci_driver gem_driver = { + .name = GEM_MODULE_NAME, + .id_table = gem_pci_tbl, + .probe = gem_init_one, + .remove = __devexit_p(gem_remove_one), +#ifdef CONFIG_PM + .suspend = gem_suspend, + .resume = gem_resume, +#endif /* CONFIG_PM */ +}; + +static int __init gem_init(void) +{ + return pci_module_init(&gem_driver); +} + +static void __exit gem_cleanup(void) +{ + pci_unregister_driver(&gem_driver); +} + +module_init(gem_init); +module_exit(gem_cleanup); |