/drivers/net/sungem.c
C | 3197 lines | 2251 code | 555 blank | 391 comment | 452 complexity | 3cf469f6a726c33785a36de37468f45b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
- /* $Id: sungem.c,v 1.44.2.22 2002/03/13 01:18:12 davem Exp $
- * sungem.c: Sun GEM ethernet driver.
- *
- * Copyright (C) 2000, 2001, 2002, 2003 David S. Miller (davem@redhat.com)
- *
- * Support for Apple GMAC and assorted PHYs, WOL, Power Management
- * (C) 2001,2002,2003 Benjamin Herrenscmidt (benh@kernel.crashing.org)
- * (C) 2004,2005 Benjamin Herrenscmidt, IBM Corp.
- *
- * NAPI and NETPOLL support
- * (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
- *
- * TODO:
- * - Now that the driver was significantly simplified, I need to rework
- * the locking. I'm sure we don't need _2_ spinlocks, and we probably
- * can avoid taking most of them for so long period of time (and schedule
- * instead). The main issues at this point are caused by the netdev layer
- * though:
- *
- * gem_change_mtu() and gem_set_multicast() are called with a read_lock()
- * help by net/core/dev.c, thus they can't schedule. That means they can't
- * call napi_disable() neither, thus force gem_poll() to keep a spinlock
- * where it could have been dropped. change_mtu especially would love also to
- * be able to msleep instead of horrid locked delays when resetting the HW,
- * but that read_lock() makes it impossible, unless I defer it's action to
- * the reset task, which means it'll be asynchronous (won't take effect until
- * the system schedules a bit).
- *
- * Also, it would probably be possible to also remove most of the long-life
- * locking in open/resume code path (gem_reinit_chip) by beeing more careful
- * about when we can start taking interrupts or get xmit() called...
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #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/sched.h>
- #include <linux/string.h>
- #include <linux/delay.h>
- #include <linux/init.h>
- #include <linux/errno.h>
- #include <linux/pci.h>
- #include <linux/dma-mapping.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/skbuff.h>
- #include <linux/mii.h>
- #include <linux/ethtool.h>
- #include <linux/crc32.h>
- #include <linux/random.h>
- #include <linux/workqueue.h>
- #include <linux/if_vlan.h>
- #include <linux/bitops.h>
- #include <linux/mutex.h>
- #include <linux/mm.h>
- #include <linux/gfp.h>
- #include <asm/system.h>
- #include <asm/io.h>
- #include <asm/byteorder.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- #ifdef CONFIG_SPARC
- #include <asm/idprom.h>
- #include <asm/prom.h>
- #endif
- #ifdef CONFIG_PPC_PMAC
- #include <asm/pci-bridge.h>
- #include <asm/prom.h>
- #include <asm/machdep.h>
- #include <asm/pmac_feature.h>
- #endif
- #include "sungem_phy.h"
- #include "sungem.h"
- /* Stripping FCS is causing problems, disabled for now */
- #undef STRIP_FCS
- #define DEFAULT_MSG (NETIF_MSG_DRV | \
- NETIF_MSG_PROBE | \
- NETIF_MSG_LINK)
- #define ADVERTISE_MASK (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
- SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
- SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | \
- SUPPORTED_Pause | SUPPORTED_Autoneg)
- #define DRV_NAME "sungem"
- #define DRV_VERSION "0.98"
- #define DRV_RELDATE "8/24/03"
- #define DRV_AUTHOR "David S. Miller (davem@redhat.com)"
- static char version[] __devinitdata =
- DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
- MODULE_AUTHOR(DRV_AUTHOR);
- MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");
- MODULE_LICENSE("GPL");
- #define GEM_MODULE_NAME "gem"
- static DEFINE_PCI_DEVICE_TABLE(gem_pci_tbl) = {
- { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_GEM,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- /* These models only differ from the original GEM in
- * that their tx/rx fifos are of a different size and
- * they only support 10/100 speeds. -DaveM
- *
- * Apple's GMAC does support gigabit on machines with
- * the BCM54xx PHYs. -BenH
- */
- { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_RIO_GEM,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID2_GMAC,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- {0, }
- };
- MODULE_DEVICE_TABLE(pci, gem_pci_tbl);
- static u16 __phy_read(struct gem *gp, int phy_addr, int reg)
- {
- u32 cmd;
- int limit = 10000;
- cmd = (1 << 30);
- cmd |= (2 << 28);
- cmd |= (phy_addr << 23) & MIF_FRAME_PHYAD;
- cmd |= (reg << 18) & MIF_FRAME_REGAD;
- cmd |= (MIF_FRAME_TAMSB);
- writel(cmd, gp->regs + MIF_FRAME);
- while (--limit) {
- cmd = readl(gp->regs + MIF_FRAME);
- if (cmd & MIF_FRAME_TALSB)
- break;
- udelay(10);
- }
- if (!limit)
- cmd = 0xffff;
- return cmd & MIF_FRAME_DATA;
- }
- static inline int _phy_read(struct net_device *dev, int mii_id, int reg)
- {
- struct gem *gp = netdev_priv(dev);
- 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 = netdev_priv(dev);
- __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 */