diff options
author | davem <davem> | 2001-11-29 03:57:33 +0000 |
---|---|---|
committer | davem <davem> | 2001-11-29 03:57:33 +0000 |
commit | f88d4da353eeb4a4a1f76289325d984dd16048c0 (patch) | |
tree | 5fd6ea386adb166f5f2c8cd321e985caf7c4c790 | |
parent | d48c56b644a9d4bd7d83a9e997afb4d85241d92c (diff) | |
download | netdev-vger-cvs-f88d4da353eeb4a4a1f76289325d984dd16048c0.tar.gz |
PPC support updates from Banjamin Herrenscmidt
-rw-r--r-- | drivers/net/sungem.c | 1040 | ||||
-rw-r--r-- | drivers/net/sungem.h | 95 |
2 files changed, 893 insertions, 242 deletions
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 08bd48bea..48a184e22 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1,7 +1,15 @@ -/* $Id: sungem.c,v 1.34 2001-11-28 21:05:31 davem Exp $ +/* $Id: sungem.c,v 1.35 2001-11-29 03:57:33 davem Exp $ * sungem.c: Sun GEM ethernet driver. * * Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) + * + * Support for Apple GMAC and assorted PHYs by + * Benjamin Herrenscmidt (benh@kernel.crashing.org) + * + * TODO: + * - Get rid of all those nasty mdelay's and replace them + * with schedule_timeout. + * - Implement WOL */ #include <linux/module.h> @@ -23,11 +31,15 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/irq.h> #include <asm/system.h> #include <asm/bitops.h> #include <asm/io.h> #include <asm/byteorder.h> +#include <asm/uaccess.h> #ifdef __sparc__ #include <asm/idprom.h> @@ -45,15 +57,33 @@ #include "sungem.h" +#define DRV_NAME "sungem" +#define DRV_VERSION "0.96" +#define DRV_RELDATE "11/17/01" +#define DRV_AUTHOR "David S. Miller (davem@redhat.com)" + static char version[] __devinitdata = - "sungem.c:v0.95 16/Oct/01 David S. Miller (davem@redhat.com)\n"; + DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n"; -MODULE_AUTHOR("David S. Miller (davem@redhat.com)"); +MODULE_AUTHOR(DRV_AUTHOR); MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver"); MODULE_LICENSE("GPL"); MODULE_PARM(gem_debug, "i"); MODULE_PARM_DESC(gem_debug, "(ignored)"); +MODULE_PARM(link_mode, "i"); + +static int link_mode; + +static u16 link_modes[] __devinitdata = { + BMCR_ANENABLE, /* 0 : autoneg */ + 0, /* 1 : 10bt half duplex */ + BMCR_SPEED100, /* 2 : 100bt half duplex */ + BMCR_SPD2, /* verify this */ /* 3 : 1000bt half duplex */ + BMCR_FULLDPLX, /* 4 : 10bt full duplex */ + BMCR_SPEED100|BMCR_FULLDPLX, /* 5 : 100bt full duplex */ + BMCR_SPD2|BMCR_FULLDPLX /* 6 : 1000bt full duplex */ +}; #define GEM_MODULE_NAME "gem" #define PFX GEM_MODULE_NAME ": " @@ -73,7 +103,7 @@ static struct pci_device_id gem_pci_tbl[] __devinitdata = { * they only support 10/100 speeds. -DaveM * * Apple's GMAC does support gigabit on machines with - * the BCM5400 or 5401 PHYs. -BenH + * the BCM54xx PHYs. -BenH */ { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_RIO_GEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, @@ -362,10 +392,6 @@ static int gem_pci_interrupt(struct net_device *dev, struct gem *gp, u32 gem_sta return 1; } -static void gem_stop(struct gem *, unsigned long); -static void gem_init_rings(struct gem *, int); -static void gem_init_hw(struct gem *); - /* 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 @@ -418,9 +444,9 @@ static int gem_abnormal_irq(struct net_device *dev, struct gem *gp, u32 gem_stat return 0; do_reset: - gem_stop(gp, gp->regs); - gem_init_rings(gp, 1); - gem_init_hw(gp); + gp->reset_task_pending = 1; + schedule_task(&gp->reset_task); + return 1; } @@ -627,6 +653,10 @@ 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->hw_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), @@ -640,13 +670,10 @@ static void gem_tx_timeout(struct net_device *dev) spin_lock_irq(&gp->lock); - gem_stop(gp, gp->regs); - gem_init_rings(gp, 1); - gem_init_hw(gp); + gp->reset_task_pending = 1; + schedule_task(&gp->reset_task); spin_unlock_irq(&gp->lock); - - netif_wake_queue(dev); } static __inline__ int gem_intme(int entry) @@ -781,18 +808,19 @@ static int gem_change_mtu(struct net_device *dev, int new_mtu) return -EINVAL; spin_lock_irq(&gp->lock); - gem_stop(gp, gp->regs); dev->mtu = new_mtu; - gem_init_rings(gp, 1); - gem_init_hw(gp); + gp->reset_task_pending = 1; + schedule_task(&gp->reset_task); spin_unlock_irq(&gp->lock); + flush_scheduled_tasks(); + return 0; } #define STOP_TRIES 32 -static void gem_stop(struct gem *gp, unsigned long regs) +static void gem_stop(struct gem *gp) { int limit; u32 val; @@ -801,13 +829,13 @@ static void gem_stop(struct gem *gp, unsigned long regs) writel(0xffffffff, gp->regs + GREG_IMASK); /* Reset the chip */ - writel(GREG_SWRST_TXRST | GREG_SWRST_RXRST, regs + GREG_SWRST); + writel(GREG_SWRST_TXRST | GREG_SWRST_RXRST, gp->regs + GREG_SWRST); limit = STOP_TRIES; do { udelay(20); - val = readl(regs + GREG_SWRST); + val = readl(gp->regs + GREG_SWRST); if (limit-- <= 0) break; } while (val & (GREG_SWRST_TXRST | GREG_SWRST_RXRST)); @@ -830,6 +858,9 @@ static void gem_start_dma(struct gem *gp) val = readl(gp->regs + MAC_RXCFG); writel(val | MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG); + (void) readl(gp->regs + MAC_RXCFG); + udelay(100); + writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK); writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK); @@ -848,6 +879,88 @@ static int phy_BCM5400_link_table[8][3] = { { 1, 0, 1 }, /* 1000BT */ }; +static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep) +{ + u16 ctl; + + /* Setup link parameters */ + if (!ep) + goto start_aneg; + if (ep->autoneg == AUTONEG_ENABLE) { + /* TODO: parse ep->advertising */ + gp->link_advertise |= (ADVERTISE_10HALF | ADVERTISE_10FULL); + gp->link_advertise |= (ADVERTISE_100HALF | ADVERTISE_100FULL); + /* Can I advertise gigabit here ? I'd need BCM PHY docs... */ + gp->link_cntl = BMCR_ANENABLE; + } else { + gp->link_cntl = 0; + if (ep->speed == SPEED_100) + gp->link_cntl |= BMCR_SPEED100; + else if (ep->speed == SPEED_1000 && gp->gigabit_capable) + /* Hrm... check if this is right... */ + gp->link_cntl |= BMCR_SPD2; + if (ep->duplex == DUPLEX_FULL) + gp->link_cntl |= BMCR_FULLDPLX; + } + +start_aneg: + spin_lock_irq(&gp->lock); + if (!gp->hw_running) { + spin_unlock_irq(&gp->lock); + return; + } + + /* Configure PHY & start aneg */ + ctl = phy_read(gp, MII_BMCR); + ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE); + ctl |= gp->link_cntl; + if (ctl & BMCR_ANENABLE) { + ctl |= BMCR_ANRESTART; + gp->lstate = link_aneg; + } else { + gp->lstate = link_force_ok; + } + phy_write(gp, MII_BMCR, ctl); + + gp->timer_ticks = 0; + gp->link_timer.expires = jiffies + ((12 * HZ) / 10); + add_timer(&gp->link_timer); + spin_unlock_irq(&gp->lock); +} + +static void gem_read_mii_link_mode(struct gem *gp, int *fd, int *spd, int *pause) +{ + u32 val; + + *fd = 0; + *spd = 10; + *pause = 0; + + if (gp->phy_mod == phymod_bcm5400 || + gp->phy_mod == phymod_bcm5401 || + gp->phy_mod == phymod_bcm5411) { + int link_mode; + + val = phy_read(gp, MII_BCM5400_AUXSTATUS); + link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >> + MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT); + *fd = phy_BCM5400_link_table[link_mode][0]; + *spd = phy_BCM5400_link_table[link_mode][2] ? + 1000 : + (phy_BCM5400_link_table[link_mode][1] ? 100 : 10); + val = phy_read(gp, MII_LPA); + if (val & LPA_PAUSE) + *pause = 1; + } else { + val = phy_read(gp, MII_LPA); + + if (val & (LPA_10FULL | LPA_100FULL)) + *fd = 1; + if (val & (LPA_100FULL | LPA_100HALF)) + *spd = 100; + } +} + /* A link-up condition has occurred, initialize and enable the * rest of the chip. */ @@ -862,32 +975,13 @@ static void gem_set_link_modes(struct gem *gp) if (gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) { - if (gp->lstate == aneg_wait) { - if (gp->phy_mod == phymod_bcm5400 || - gp->phy_mod == phymod_bcm5401 || - gp->phy_mod == phymod_bcm5411) { - int link_mode; - val = phy_read(gp, PHY_BCM5400_AUXSTATUS); - link_mode = (val & PHY_BCM5400_AUXSTATUS_LINKMODE_MASK) >> - PHY_BCM5400_AUXSTATUS_LINKMODE_SHIFT; - full_duplex = phy_BCM5400_link_table[link_mode][0]; - speed = phy_BCM5400_link_table[link_mode][2] ? 1000 - : (phy_BCM5400_link_table[link_mode][1] ? 100 : 10); - val = phy_read(gp, PHY_LPA); - if (val & PHY_LPA_PAUSE) - pause = 1; - } else { - val = phy_read(gp, PHY_LPA); - if (val & (PHY_LPA_10FULL | PHY_LPA_100FULL)) - full_duplex = 1; - if (val & (PHY_LPA_100FULL | PHY_LPA_100HALF)) - speed = 100; - } - } else { - val = phy_read(gp, PHY_CTRL); - if (val & PHY_CTRL_FDPLX) + val = phy_read(gp, MII_BMCR); + if (val & BMCR_ANENABLE) + gem_read_mii_link_mode(gp, &full_duplex, &speed, &pause); + else { + if (val & BMCR_FULLDPLX) full_duplex = 1; - if (val & PHY_CTRL_SPD100) + if (val & BMCR_SPEED100) speed = 100; } } else { @@ -964,56 +1058,136 @@ static void gem_set_link_modes(struct gem *gp) static int gem_mdio_link_not_up(struct gem *gp) { - if (gp->lstate == aneg_wait) { - u16 val = phy_read(gp, PHY_CTRL); + if (gp->lstate == link_force_ret) { + printk(KERN_INFO "%s: Autoneg failed again, keeping" + " forced mode\n", gp->dev->name); + phy_write(gp, MII_BMCR, gp->link_fcntl); + gp->timer_ticks = 5; + gp->lstate = link_force_ok; + } else if (gp->lstate == link_aneg) { + u16 val = phy_read(gp, MII_BMCR); /* Try forced modes. */ - val &= ~(PHY_CTRL_ANRES | PHY_CTRL_ANENAB); - val &= ~(PHY_CTRL_FDPLX); - val |= PHY_CTRL_SPD100; - phy_write(gp, PHY_CTRL, val); - gp->timer_ticks = 0; - gp->lstate = force_wait; - return 1; + val &= ~(BMCR_ANRESTART | BMCR_ANENABLE); + val &= ~(BMCR_FULLDPLX); + val |= BMCR_SPEED100; + phy_write(gp, MII_BMCR, val); + gp->timer_ticks = 5; + gp->lstate = link_force_try; } else { /* Downgrade from 100 to 10 Mbps if necessary. * If already at 10Mbps, warn user about the * situation every 10 ticks. */ - u16 val = phy_read(gp, PHY_CTRL); - if (val & PHY_CTRL_SPD100) { - val &= ~PHY_CTRL_SPD100; - phy_write(gp, PHY_CTRL, val); - gp->timer_ticks = 0; - return 1; + u16 val = phy_read(gp, MII_BMCR); + if (val & BMCR_SPEED100) { + val &= ~BMCR_SPEED100; + phy_write(gp, MII_BMCR, val); + gp->timer_ticks = 5; } else { - printk(KERN_ERR "%s: Link down, cable problem?\n", - gp->dev->name); - val |= (PHY_CTRL_ANRES | PHY_CTRL_ANENAB); - phy_write(gp, PHY_CTRL, val); - gp->timer_ticks = 1; - gp->lstate = aneg_wait; return 1; } } + return 0; +} + +static void gem_init_rings(struct gem *, int); +static void gem_init_hw(struct gem *, int); + +static void gem_reset_task(void *data) +{ + struct gem *gp = (struct gem *) data; + + /* The link went down, we reset the ring, but keep + * DMA stopped. Todo: Use this function for reset + * on error as well. + */ + if (gp->hw_running && gp->opened) { + /* Make sure we don't get interrupts or tx packets */ + spin_lock_irq(&gp->lock); + + netif_stop_queue(gp->dev); + + writel(0xffffffff, gp->regs + GREG_IMASK); + + spin_unlock_irq(&gp->lock); + + /* Reset the chip & rings */ + gem_stop(gp); + gem_init_rings(gp, 0); + gem_init_hw(gp, 0); + + netif_wake_queue(gp->dev); + } + gp->reset_task_pending = 0; } static void gem_link_timer(unsigned long data) { struct gem *gp = (struct gem *) data; - int restart_timer = 0; - gp->timer_ticks++; + if (!gp->hw_running) + return; + + /* If the link of task is still pending, we just + * reschedule the link timer + */ + if (gp->reset_task_pending) + goto restart; + + spin_lock_irq(&gp->lock); if (gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) { - u16 val = phy_read(gp, PHY_STAT); + u16 val = phy_read(gp, MII_BMSR); + u16 cntl = phy_read(gp, MII_BMCR); + int up; - if (val & PHY_STAT_LSTAT) { - gem_set_link_modes(gp); - } else if (gp->timer_ticks < 10) { - restart_timer = 1; + /* When using autoneg, we really wait for ANEGCOMPLETE or we may + * get a "transcient" incorrect link state + */ + if (cntl & BMCR_ANENABLE) + up = (val & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS); + else + up = (val & BMSR_LSTATUS) != 0; + if (up) { + /* 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->link_cntl & BMCR_ANENABLE)) { + gp->lstate = link_force_ret; + gp->link_fcntl = phy_read(gp, MII_BMCR); + gp->timer_ticks = 5; + printk(KERN_INFO "%s: Got link after fallback, retrying autoneg" + " once...\n", gp->dev->name); + phy_write(gp, MII_BMCR, + gp->link_fcntl | BMCR_ANENABLE | BMCR_ANRESTART); + } else if (gp->lstate != link_up) { + gp->lstate = link_up; + if (gp->opened) + gem_set_link_modes(gp); + } } else { - restart_timer = gem_mdio_link_not_up(gp); + int restart = 0; + + /* If the link was previously up, we restart the + * whole process + */ + if (gp->lstate == link_up) { + gp->lstate = link_down; + printk(KERN_INFO "%s: Link down\n", gp->dev->name); + gp->reset_task_pending = 1; + schedule_task(&gp->reset_task); + restart = 1; + } else if (++gp->timer_ticks > 10) + restart = gem_mdio_link_not_up(gp); + + if (restart) { + spin_unlock_irq(&gp->lock); + gem_begin_auto_negotiation(gp, NULL); + return; + } } } else { u32 val = readl(gp->regs + PCS_MIISTAT); @@ -1021,17 +1195,17 @@ static void gem_link_timer(unsigned long data) if (!(val & PCS_MIISTAT_LS)) val = readl(gp->regs + PCS_MIISTAT); - if ((val & PCS_MIISTAT_LS) == 0) { - restart_timer = 1; - } else { - gem_set_link_modes(gp); + if ((val & PCS_MIISTAT_LS) != 0) { + gp->lstate = link_up; + if (gp->opened) + gem_set_link_modes(gp); } } - if (restart_timer) { - gp->link_timer.expires = jiffies + ((12 * HZ) / 10); - add_timer(&gp->link_timer); - } +restart: + gp->link_timer.expires = jiffies + ((12 * HZ) / 10); + add_timer(&gp->link_timer); + spin_unlock_irq(&gp->lock); } static void gem_clean_rings(struct gem *gp) @@ -1128,66 +1302,72 @@ static void gem_init_rings(struct gem *gp, int from_irq) } } -static int -gem_reset_one_mii_phy(struct gem *gp, int phy_addr) +static int gem_reset_one_mii_phy(struct gem *gp, int phy_addr) { u16 val; int limit = 10000; - val = __phy_read(gp, PHY_CTRL, phy_addr); - val &= ~PHY_CTRL_ISO; - val |= PHY_CTRL_RST; - __phy_write(gp, PHY_CTRL, val, phy_addr); + val = __phy_read(gp, MII_BMCR, phy_addr); + val &= ~BMCR_ISOLATE; + val |= BMCR_RESET; + __phy_write(gp, MII_BMCR, val, phy_addr); udelay(100); while (limit--) { - val = __phy_read(gp, PHY_CTRL, phy_addr); - if ((val & PHY_CTRL_RST) == 0) + val = __phy_read(gp, MII_BMCR, phy_addr); + if ((val & BMCR_RESET) == 0) break; udelay(10); } - if ((val & PHY_CTRL_ISO) && limit > 0) - __phy_write(gp, PHY_CTRL, val & ~PHY_CTRL_ISO, phy_addr); + if ((val & BMCR_ISOLATE) && limit > 0) + __phy_write(gp, MII_BMCR, val & ~BMCR_ISOLATE, phy_addr); return (limit <= 0); } -static void -gem_init_bcm5400_phy(struct gem *gp) +static void gem_init_bcm5201_phy(struct gem *gp) +{ + u16 data; + + data = phy_read(gp, MII_BCM5201_MULTIPHY); + data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE; + phy_write(gp, MII_BCM5201_MULTIPHY, data); +} + +static void gem_init_bcm5400_phy(struct gem *gp) { u16 data; /* Configure for gigabit full duplex */ - data = phy_read(gp, PHY_BCM5400_AUXCONTROL); - data |= PHY_BCM5400_AUXCONTROL_PWR10BASET; - phy_write(gp, PHY_BCM5400_AUXCONTROL, data); + data = phy_read(gp, MII_BCM5400_AUXCONTROL); + data |= MII_BCM5400_AUXCONTROL_PWR10BASET; + phy_write(gp, MII_BCM5400_AUXCONTROL, data); - data = phy_read(gp, PHY_BCM5400_GB_CONTROL); - data |= PHY_BCM5400_GB_CONTROL_FULLDUPLEXCAP; - phy_write(gp, PHY_BCM5400_GB_CONTROL, data); + data = phy_read(gp, MII_BCM5400_GB_CONTROL); + data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; + phy_write(gp, MII_BCM5400_GB_CONTROL, data); mdelay(10); /* Reset and configure cascaded 10/100 PHY */ gem_reset_one_mii_phy(gp, 0x1f); - data = __phy_read(gp, PHY_BCM5201_MULTIPHY, 0x1f); - data |= PHY_BCM5201_MULTIPHY_SERIALMODE; - __phy_write(gp, PHY_BCM5201_MULTIPHY, data, 0x1f); + data = __phy_read(gp, MII_BCM5201_MULTIPHY, 0x1f); + data |= MII_BCM5201_MULTIPHY_SERIALMODE; + __phy_write(gp, MII_BCM5201_MULTIPHY, data, 0x1f); - data = phy_read(gp, PHY_BCM5400_AUXCONTROL); - data &= ~PHY_BCM5400_AUXCONTROL_PWR10BASET; - phy_write(gp, PHY_BCM5400_AUXCONTROL, data); + data = phy_read(gp, MII_BCM5400_AUXCONTROL); + data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET; + phy_write(gp, MII_BCM5400_AUXCONTROL, data); } -static void -gem_init_bcm5401_phy(struct gem *gp) +static void gem_init_bcm5401_phy(struct gem *gp) { u16 data; int rev; - rev = phy_read(gp, PHY_ID1) & 0x000f; + rev = phy_read(gp, MII_PHYSID2) & 0x000f; if (rev == 0 || rev == 3) { /* Some revisions of 5401 appear to need this * initialisation sequence to disable, according @@ -1211,22 +1391,21 @@ gem_init_bcm5401_phy(struct gem *gp) } /* Configure for gigabit full duplex */ - data = phy_read(gp, PHY_BCM5400_GB_CONTROL); - data |= PHY_BCM5400_GB_CONTROL_FULLDUPLEXCAP; - phy_write(gp, PHY_BCM5400_GB_CONTROL, data); + data = phy_read(gp, MII_BCM5400_GB_CONTROL); + data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; + phy_write(gp, MII_BCM5400_GB_CONTROL, data); mdelay(1); /* Reset and configure cascaded 10/100 PHY */ gem_reset_one_mii_phy(gp, 0x1f); - data = __phy_read(gp, PHY_BCM5201_MULTIPHY, 0x1f); - data |= PHY_BCM5201_MULTIPHY_SERIALMODE; - __phy_write(gp, PHY_BCM5201_MULTIPHY, data, 0x1f); + data = __phy_read(gp, MII_BCM5201_MULTIPHY, 0x1f); + data |= MII_BCM5201_MULTIPHY_SERIALMODE; + __phy_write(gp, MII_BCM5201_MULTIPHY, data, 0x1f); } -static void -gem_init_bcm5411_phy(struct gem *gp) +static void gem_init_bcm5411_phy(struct gem *gp) { u16 data; @@ -1240,20 +1419,30 @@ gem_init_bcm5411_phy(struct gem *gp) /* Here, Apple seems to want to reset it, do * it as well */ - phy_write(gp, PHY_CTRL, PHY_CTRL_RST); + phy_write(gp, MII_BMCR, BMCR_RESET); /* Start autoneg */ - phy_write(gp, PHY_CTRL, - (PHY_CTRL_ANENAB | PHY_CTRL_FDPLX | - PHY_CTRL_ANRES | PHY_CTRL_SPD2)); + phy_write(gp, MII_BMCR, + (BMCR_ANENABLE | BMCR_FULLDPLX | + BMCR_ANRESTART | BMCR_SPD2)); - data = phy_read(gp, PHY_BCM5400_GB_CONTROL); - data |= PHY_BCM5400_GB_CONTROL_FULLDUPLEXCAP; - phy_write(gp, PHY_BCM5400_GB_CONTROL, data); + data = phy_read(gp, MII_BCM5400_GB_CONTROL); + data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP; + phy_write(gp, MII_BCM5400_GB_CONTROL, data); } static void gem_init_phy(struct gem *gp) { + u32 mifcfg; + + if (!gp->wake_on_lan && gp->phy_mod == phymod_bcm5201) + phy_write(gp, MII_BCM5201_INTERRUPT, 0); + + /* Revert MIF CFG setting done on stop_phy */ + mifcfg = readl(gp->regs + MIF_CFG); + mifcfg &= ~MIF_CFG_BBMODE; + writel(mifcfg, gp->regs + MIF_CFG); + #ifdef CONFIG_ALL_PPC if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { int i; @@ -1261,7 +1450,7 @@ static void gem_init_phy(struct gem *gp) pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0); for (i = 0; i < 32; i++) { gp->mii_phy_addr = i; - if (phy_read(gp, PHY_CTRL) != 0xffff) + if (phy_read(gp, MII_CTRL) != 0xffff) break; } if (i == 32) { @@ -1297,12 +1486,13 @@ static void gem_init_phy(struct gem *gp) /* Take PHY out of isloate mode and reset it. */ gem_reset_one_mii_phy(gp, gp->mii_phy_addr); - phy_id = (phy_read(gp, PHY_ID0) << 16 | phy_read(gp, PHY_ID1)) + phy_id = (phy_read(gp, MII_PHYSID1) << 16 | phy_read(gp, MII_PHYSID2)) & 0xfffffff0; printk(KERN_INFO "%s: MII PHY ID: %x ", gp->dev->name, phy_id); switch(phy_id) { case 0x406210: gp->phy_mod = phymod_bcm5201; + gem_init_bcm5201_phy(gp); printk("BCM 5201\n"); break; case 0x4061e0: @@ -1313,16 +1503,19 @@ static void gem_init_phy(struct gem *gp) printk("BCM 5400\n"); gp->phy_mod = phymod_bcm5400; gem_init_bcm5400_phy(gp); + gp->gigabit_capable = 1; break; case 0x206050: printk("BCM 5401\n"); gp->phy_mod = phymod_bcm5401; gem_init_bcm5401_phy(gp); + gp->gigabit_capable = 1; break; case 0x206070: printk("BCM 5411\n"); gp->phy_mod = phymod_bcm5411; gem_init_bcm5411_phy(gp); + gp->gigabit_capable = 1; break; default: printk("Generic\n"); @@ -1330,21 +1523,15 @@ static void gem_init_phy(struct gem *gp) }; /* Init advertisement and enable autonegotiation. */ - val = phy_read(gp, PHY_CTRL); - val &= ~PHY_CTRL_ANENAB; - phy_write(gp, PHY_CTRL, val); + val = phy_read(gp, MII_BMCR); + val &= ~BMCR_ANENABLE; + phy_write(gp, MII_BMCR, val); udelay(10); - phy_write(gp, PHY_ADV, - phy_read(gp, PHY_ADV) | - (PHY_ADV_10HALF | PHY_ADV_10FULL | - PHY_ADV_100HALF | PHY_ADV_100FULL)); - - val = phy_read(gp, PHY_CTRL); - val |= PHY_CTRL_ANENAB; - phy_write(gp, PHY_CTRL, val); - val |= PHY_CTRL_ANRES; - phy_write(gp, PHY_CTRL, val); + phy_write(gp, MII_ADVERTISE, + phy_read(gp, MII_ADVERTISE) | + (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL)); } else { u32 val; int limit; @@ -1399,6 +1586,7 @@ static void gem_init_phy(struct gem *gp) else val |= PCS_SCTRL_LOOP; writel(val, gp->regs + PCS_SCTRL); + gp->gigabit_capable = 1; } } @@ -1584,8 +1772,7 @@ static void gem_init_mac(struct gem *gp) writel(0xffffffff, gp->regs + MAC_MCMASK); } -static void -gem_init_pause_thresholds(struct gem* gp) +static void gem_init_pause_thresholds(struct gem *gp) { /* Calculate pause thresholds. Setting the OFF threshold to the * full RX fifo size effectively disables PAUSE generation which @@ -1617,18 +1804,13 @@ static int gem_check_invariants(struct gem *gp) struct pci_dev *pdev = gp->pdev; u32 mif_cfg; - /* On Apple's sungem, we can't realy on registers as the chip + /* On Apple's sungem, we can't rely on registers as the chip * was been powered down by the firmware. We do the PHY lookup * when the interface is opened and we configure the driver * with known values. */ if (pdev->vendor == PCI_VENDOR_ID_APPLE) { gp->phy_type = phy_mii_mdio0; - mif_cfg = readl(gp->regs + MIF_CFG); - mif_cfg &= ~MIF_CFG_PSELECT; - writel(mif_cfg, gp->regs + MIF_CFG); - writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE); - writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG); gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64; gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64; gem_init_pause_thresholds(gp); @@ -1670,7 +1852,7 @@ static int gem_check_invariants(struct gem *gp) for (i = 0; i < 32; i++) { gp->mii_phy_addr = i; - if (phy_read(gp, PHY_CTRL) != 0xffff) + if (phy_read(gp, MII_BMCR) != 0xffff) break; } if (i == 32) { @@ -1709,7 +1891,7 @@ static int gem_check_invariants(struct gem *gp) return 0; } -static void gem_init_hw(struct gem *gp) +static void gem_init_hw(struct gem *gp, int restart_link) { /* On Apple's gmac, I initialize the PHY only after * setting up the chip. It appears the gigabit PHYs @@ -1717,18 +1899,23 @@ static void gem_init_hw(struct gem *gp) * the chip is not running, I suspect it might not * be clocked at that point. --BenH */ - if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { - gem_check_invariants(gp); - gp->hw_running = 1; - } - gem_init_phy(gp); + if (restart_link) + gem_init_phy(gp); gem_init_dma(gp); gem_init_mac(gp); - gp->timer_ticks = 0; - gp->lstate = aneg_wait; - gp->link_timer.expires = jiffies + ((12 * HZ) / 10); - add_timer(&gp->link_timer); + spin_lock_irq(&gp->lock); + if (restart_link) { + /* Default aneg parameters */ + gp->timer_ticks = 0; + gp->lstate = link_down; + /* Can I advertise gigabit here ? I'd need BCM PHY docs... */ + gem_begin_auto_negotiation(gp, NULL); + } else { + if (gp->lstate == link_up) + gem_set_link_modes(gp); + } + spin_unlock_irq(&gp->lock); } #ifdef CONFIG_ALL_PPC @@ -1736,10 +1923,10 @@ static void gem_init_hw(struct gem *gp) * setup properly. There appear to be no need to restore the * base addresses. */ -static void -gem_apple_powerup(struct gem* gp) +static void gem_apple_powerup(struct gem *gp) { u16 cmd; + u32 mif_cfg; pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1); @@ -1750,31 +1937,161 @@ gem_apple_powerup(struct gem* gp) pci_write_config_word(gp->pdev, PCI_COMMAND, cmd); pci_write_config_byte(gp->pdev, PCI_LATENCY_TIMER, 6); pci_write_config_byte(gp->pdev, PCI_CACHE_LINE_SIZE, 8); + + mdelay(1); + + mif_cfg = readl(gp->regs + MIF_CFG); + mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|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); + + mdelay(1); } /* Turn off the chip's clock */ -static void -gem_apple_powerdown(struct gem* gp) +static void gem_apple_powerdown(struct gem *gp) { pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0); } + #endif /* CONFIG_ALL_PPC */ +static void gem_stop_phy(struct gem *gp) +{ + u32 mifcfg; + + if (!gp->wake_on_lan && gp->phy_mod == phymod_bcm5201) + phy_write(gp, MII_BCM5201_INTERRUPT, 0); + + /* 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); + + /* Here's a strange hack used by both MacOS 9 and X */ + phy_write(gp, MII_LPA, phy_read(gp, MII_LPA)); + + if (gp->wake_on_lan) { + /* Setup wake-on-lan */ + } else + writel(0, gp->regs + MAC_RXCFG); + 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 (!gp->wake_on_lan) { + gem_stop(gp); + writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST); + writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST); + if (gp->phy_mod == phymod_bcm5400 || gp->phy_mod == phymod_bcm5401 || + gp->phy_mod == phymod_bcm5411) { +#if 0 /* Commented out in Darwin... someone has those dawn docs ? */ + phy_write(gp, MII_BMCR, BMCR_PDOWN); +#endif + } else if (gp->phy_mod == phymod_bcm5201 || gp->phy_mod == phymod_bcm5221) { +#if 0 /* Commented out in Darwin... someone has those dawn docs ? */ + u16 val = phy_read(gp, MII_BCM5201_AUXMODE2) + phy_write(gp, MII_BCM5201_AUXMODE2, + val & ~MII_BCM5201_AUXMODE2_LOWPOWER); +#endif + phy_write(gp, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE); + } + + /* 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); + } +} + +/* Shut down the chip, must be called with pm_sem held. */ +static void gem_shutdown(struct gem *gp) +{ + /* Make us not-running to avoid timers respawning */ + gp->hw_running = 0; + + /* Stop the link timer */ + del_timer_sync(&gp->link_timer); + + /* Stop the reset task */ + while (gp->reset_task_pending) + schedule(); + + /* Actually stop the chip */ + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) + gem_stop_phy(gp); + else + gem_stop(gp); + +#ifdef CONFIG_ALL_PPC + /* Power down the chip */ + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) + gem_apple_powerdown(gp); +#endif /* CONFIG_ALL_PPC */ +} + +static void gem_pm_task(void *data) +{ + struct gem *gp = (struct gem *) data; + + /* We assume if we can't lock the pm_sem, then open() was + * called again (or suspend()), and we can safely ignore + * the PM request + */ + if (down_trylock(&gp->pm_sem)) + return; + + /* Driver was re-opened or already shut down */ + if (gp->opened || !gp->hw_running) { + up(&gp->pm_sem); + return; + } + + gem_shutdown(gp); + + up(&gp->pm_sem); +} + +static void gem_pm_timer(unsigned long data) +{ + struct gem *gp = (struct gem *) data; + + schedule_task(&gp->pm_task); +} + static int gem_open(struct net_device *dev) { struct gem *gp = dev->priv; - unsigned long regs = gp->regs; + int hw_was_up = gp->hw_running; + + down(&gp->pm_sem); - del_timer(&gp->link_timer); + /* Stop the PM timer/task */ + del_timer(&gp->pm_timer); + flush_scheduled_tasks(); + if (!gp->hw_running) { #ifdef CONFIG_ALL_PPC - /* First, we need to bring up the chip */ - if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) - gem_apple_powerup(gp); + /* First, we need to bring up the chip */ + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { + gem_apple_powerup(gp); + gem_check_invariants(gp); + } #endif /* CONFIG_ALL_PPC */ + /* Reset the chip */ + gem_stop(gp); - /* Reset the chip */ - gem_stop(gp, regs); + gp->hw_running = 1; + } /* We can now request the interrupt as we know it's masked * on the controller @@ -1782,9 +2099,14 @@ static int gem_open(struct net_device *dev) if (request_irq(gp->pdev->irq, gem_interrupt, SA_SHIRQ, dev->name, (void *)dev)) { #ifdef CONFIG_ALL_PPC - if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) + if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE) gem_apple_powerdown(gp); #endif /* CONFIG_ALL_PPC */ + /* Fire the PM timer that will shut us down in about 10 seconds */ + gp->pm_timer.expires = jiffies + 10*HZ; + add_timer(&gp->pm_timer); + up(&gp->pm_sem); + return -EAGAIN; } @@ -1792,7 +2114,11 @@ static int gem_open(struct net_device *dev) gem_init_rings(gp, 0); /* Init & setup chip hardware */ - gem_init_hw(gp); + gem_init_hw(gp, !hw_was_up); + + gp->opened = 1; + + up(&gp->pm_sem); return 0; } @@ -1801,17 +2127,110 @@ static int gem_close(struct net_device *dev) { struct gem *gp = dev->priv; - del_timer(&gp->link_timer); - gem_stop(gp, gp->regs); + /* Make sure we don't get distracted by suspend/resume */ + down(&gp->pm_sem); + + /* Stop traffic, mark us closed */ + spin_lock_irq(&gp->lock); + + gp->opened = 0; + writel(0xffffffff, gp->regs + GREG_IMASK); + netif_stop_queue(dev); + + spin_unlock_irq(&gp->lock); + + /* Stop chip */ + gem_stop(gp); + + /* Get rid of rings */ gem_clean_rings(gp); - gp->hw_running = 0; + + /* Bye, the pm timer will finish the job */ + free_irq(gp->pdev->irq, (void *) dev); + + /* Fire the PM timer that will shut us down in about 10 seconds */ + gp->pm_timer.expires = jiffies + 10*HZ; + add_timer(&gp->pm_timer); + + up(&gp->pm_sem); + + return 0; +} + +#ifdef CONFIG_PM +static int gem_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct gem *gp = dev->priv; + + /* We hold the PM semaphore during entire driver + * sleep time + */ + down(&gp->pm_sem); + + printk(KERN_INFO "%s: suspending, WakeOnLan %s\n", + dev->name, gp->wake_on_lan ? "enabled" : "disabled"); + + /* If the driver is opened, we stop the DMA */ + if (gp->opened) { + /* Stop traffic, mark us closed */ + netif_device_detach(dev); + + spin_lock_irq(&gp->lock); + + writel(0xffffffff, gp->regs + GREG_IMASK); + + spin_unlock_irq(&gp->lock); + + /* Stop chip */ + gem_stop(gp); + + /* Get rid of ring buffers */ + gem_clean_rings(gp); + + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) + disable_irq(gp->pdev->irq); + } + + if (gp->hw_running) { + /* Kill PM timer if any */ + del_timer_sync(&gp->pm_timer); + flush_scheduled_tasks(); + + gem_shutdown(gp); + } + + return 0; +} + +static int gem_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct gem *gp = dev->priv; + + printk(KERN_INFO "%s: resuming\n", dev->name); + + if (gp->opened) { #ifdef CONFIG_ALL_PPC - if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) - gem_apple_powerdown(gp); + /* First, we need to bring up the chip */ + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { + gem_apple_powerup(gp); + gem_check_invariants(gp); + } #endif /* CONFIG_ALL_PPC */ - free_irq(gp->pdev->irq, (void *)dev); + gem_stop(gp); + gp->hw_running = 1; + gem_init_rings(gp, 0); + gem_init_hw(gp, 1); + netif_device_attach(dev); + if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) + enable_irq(gp->pdev->irq); + } + up(&gp->pm_sem); + return 0; } +#endif /* CONFIG_PM */ static struct net_device_stats *gem_get_stats(struct net_device *dev) { @@ -1927,12 +2346,202 @@ static void gem_set_multicast(struct net_device *dev) writel(hash_table[15], gp->regs + MAC_HASH15); } + /* Hrm... we may walk on the reset task here... */ netif_wake_queue(dev); } +/* Eventually add support for changing the advertisement + * on autoneg. + */ +static int gem_ethtool_ioctl(struct net_device *dev, void *ep_user) +{ + struct gem *gp = dev->priv; + u16 bmcr; + int full_duplex, speed, pause; + struct ethtool_cmd ecmd; + + if (copy_from_user(&ecmd, ep_user, sizeof(ecmd))) + return -EFAULT; + + switch(ecmd.cmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { cmd: ETHTOOL_GDRVINFO }; + + strncpy(info.driver, DRV_NAME, ETHTOOL_BUSINFO_LEN); + strncpy(info.version, DRV_VERSION, ETHTOOL_BUSINFO_LEN); + info.fw_version[0] = '\0'; + strncpy(info.bus_info, gp->pdev->slot_name, ETHTOOL_BUSINFO_LEN); + info.regdump_len = 0; /*SUNGEM_NREGS;*/ + + if (copy_to_user(ep_user, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + + case ETHTOOL_GSET: + ecmd.supported = + (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); + + /* XXX hardcoded stuff for now */ + ecmd.port = PORT_MII; + ecmd.transceiver = XCVR_EXTERNAL; + ecmd.phy_address = 0; /* XXX fixed PHYAD */ + + /* Record PHY settings if HW is on. */ + if (gp->hw_running) { + bmcr = phy_read(gp, MII_BMCR); + gem_read_mii_link_mode(gp, &full_duplex, &speed, &pause); + } else + bmcr = 0; + if (bmcr & BMCR_ANENABLE) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.speed = speed == 10 ? SPEED_10 : (speed == 1000 ? SPEED_1000 : SPEED_100); + ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = + (bmcr & BMCR_SPEED100) ? + SPEED_100 : SPEED_10; + ecmd.duplex = + (bmcr & BMCR_FULLDPLX) ? + DUPLEX_FULL : DUPLEX_HALF; + } + if (copy_to_user(ep_user, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + + case ETHTOOL_SSET: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Verify the settings we care about. */ + if (ecmd.autoneg != AUTONEG_ENABLE && + ecmd.autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (ecmd.autoneg == AUTONEG_DISABLE && + ((ecmd.speed != SPEED_100 && + ecmd.speed != SPEED_10) || + (ecmd.duplex != DUPLEX_HALF && + ecmd.duplex != DUPLEX_FULL))) + return -EINVAL; + + /* Apply settings and restart link process */ + if (gp->hw_running) + del_timer(&gp->link_timer); + gem_begin_auto_negotiation(gp, &ecmd); + return 0; + + case ETHTOOL_NWAY_RST: + if ((gp->link_cntl & BMCR_ANENABLE) == 0) + return -EINVAL; + if (gp->hw_running) + del_timer(&gp->link_timer); + gem_begin_auto_negotiation(gp, NULL); + return 0; + + case ETHTOOL_GWOL: + case ETHTOOL_SWOL: + break; /* todo */ + + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = { cmd: ETHTOOL_GLINK }; + + edata.data = (gp->lstate == link_up); + if (copy_to_user(ep_user, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = { cmd: ETHTOOL_GMSGLVL }; + + edata.data = gem_debug; + if (copy_to_user(ep_user, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + + if (copy_from_user(&edata, ep_user, sizeof(edata))) + return -EFAULT; + gem_debug = edata.data; + return 0; + } + +#if 0 + case ETHTOOL_GREGS: { + struct ethtool_regs regs; + u32 *regbuf; + int r = 0; + + if (copy_from_user(®s, useraddr, sizeof(regs))) + return -EFAULT; + + if (regs.len > SUNGEM_NREGS) { + regs.len = SUNGEM_NREGS; + } + regs.version = 0; + if (copy_to_user(useraddr, ®s, sizeof(regs))) + return -EFAULT; + + if (!gp->hw_running) + return -ENODEV; + useraddr += offsetof(struct ethtool_regs, data); + + /* Use kmalloc to avoid bloating the stack */ + regbuf = kmalloc(4 * SUNGEM_NREGS, GFP_KERNEL); + if (!regbuf) + return -ENOMEM; + spin_lock_irq(&np->lock); + gem_get_regs(gp, regbuf); + spin_unlock_irq(&np->lock); + + if (copy_to_user(useraddr, regbuf, regs.len*sizeof(u32))) + r = -EFAULT; + kfree(regbuf); + return r; + } +#endif + }; + + return -EOPNOTSUPP; +} + static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - return -EINVAL; + struct gem *gp = dev->priv; + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; + + switch (cmd) { + case SIOCETHTOOL: + return gem_ethtool_ioctl(dev, ifr->ifr_data); + + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + data->phy_id = gp->mii_phy_addr; + /* Fallthrough... */ + + case SIOCGMIIREG: /* Read MII PHY register. */ + data->val_out = __phy_read(gp, data->reg_num & 0x1f, data->phy_id & 0x1f); + return 0; + + case SIOCSMIIREG: /* Write MII PHY register. */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + __phy_write(gp, data->reg_num & 0x1f, data->val_in, data->phy_id & 0x1f); + return 0; + }; + + return -EOPNOTSUPP; } static int __devinit gem_get_device_address(struct gem *gp) @@ -2049,6 +2658,26 @@ static int __devinit gem_init_one(struct pci_dev *pdev, gp->dev = dev; spin_lock_init(&gp->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_timer(&gp->pm_timer); + gp->pm_timer.function = gem_pm_timer; + gp->pm_timer.data = (unsigned long) gp; + + INIT_TQUEUE(&gp->pm_task, gem_pm_task, gp); + INIT_TQUEUE(&gp->reset_task, gem_reset_task, gp); + + /* Default link parameters */ + if (link_mode >= 0 && link_mode <= 6) + gp->link_cntl = link_modes[link_mode]; + else + gp->link_cntl = BMCR_ANENABLE; + gp->lstate = link_down; + gp->timer_ticks = 0; gp->regs = (unsigned long) ioremap(gemreg_base, gemreg_len); if (gp->regs == 0UL) { @@ -2057,9 +2686,26 @@ static int __devinit gem_init_one(struct pci_dev *pdev, goto err_out_free_mmio_res; } - /* On Apple's, we might not access the hardware at that point */ + /* On Apple, we power the chip up now in order for check + * invariants to work, but also because the firmware might + * not have properly shut down the PHY. + */ +#ifdef CONFIG_ALL_PPC + if (pdev->vendor == PCI_VENDOR_ID_APPLE) { + gem_apple_powerup(gp); + if (gem_check_invariants(gp)) + goto err_out_iounmap; + gem_stop(gp); + gp->hw_running = 1; + gem_init_phy(gp); + gem_begin_auto_negotiation(gp, NULL); + } +#endif + /* Non Apple hardware, we just reset the chip and check + * for invariants + */ if (pdev->vendor != PCI_VENDOR_ID_APPLE) { - gem_stop(gp, gp->regs); + gem_stop(gp); if (gem_check_invariants(gp)) goto err_out_iounmap; gp->hw_running = 1; @@ -2093,10 +2739,6 @@ static int __devinit gem_init_one(struct pci_dev *pdev, i == 5 ? ' ' : ':'); printk("\n"); - init_timer(&gp->link_timer); - gp->link_timer.function = gem_link_timer; - gp->link_timer.data = (unsigned long) gp; - dev->open = gem_open; dev->stop = gem_close; dev->hard_start_xmit = gem_start_xmit; @@ -2114,9 +2756,20 @@ static int __devinit gem_init_one(struct pci_dev *pdev, if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; + /* Fire the PM timer that will shut us down in about 10 seconds */ + gp->pm_timer.expires = jiffies + 10*HZ; + add_timer(&gp->pm_timer); + return 0; err_out_iounmap: + down(&gp->pm_sem); + /* Stop the PM timer & task */ + del_timer_sync(&gp->pm_timer); + flush_scheduled_tasks(); + if (gp->hw_running) + gem_shutdown(gp); + up(&gp->pm_sem); iounmap((void *) gp->regs); err_out_free_mmio_res: @@ -2139,6 +2792,14 @@ static void __devexit gem_remove_one(struct pci_dev *pdev) unregister_netdev(dev); + down(&gp->pm_sem); + /* Stop the PM timer & task */ + del_timer_sync(&gp->pm_timer); + flush_scheduled_tasks(); + if (gp->hw_running) + gem_shutdown(gp); + up(&gp->pm_sem); + pci_free_consistent(pdev, sizeof(struct gem_init_block), gp->init_block, @@ -2146,9 +2807,6 @@ static void __devexit gem_remove_one(struct pci_dev *pdev) iounmap((void *) gp->regs); release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); -#ifdef CONFIG_ALL_PPC - pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0); -#endif kfree(dev); pci_set_drvdata(pdev, NULL); @@ -2160,6 +2818,10 @@ static struct pci_driver gem_driver = { id_table: gem_pci_tbl, probe: gem_init_one, remove: gem_remove_one, +#ifdef CONFIG_PM + suspend: gem_suspend, + resume: gem_resume, +#endif /* CONFIG_PM */ }; static int __init gem_init(void) diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h index 7b11e621a..7b8b6d0ae 100644 --- a/drivers/net/sungem.h +++ b/drivers/net/sungem.h @@ -1,4 +1,4 @@ -/* $Id: sungem.h,v 1.9 2001-11-28 23:36:06 davem Exp $ +/* $Id: sungem.h,v 1.10 2001-11-29 03:57:33 davem Exp $ * sungem.h: Definitions for Sun GEM ethernet driver. * * Copyright (C) 2000 David S. Miller (davem@redhat.com) @@ -753,64 +753,38 @@ #define PROM_SIZE 0x0fffffUL /* Size of ROM */ #define PROM_END 0x200000UL /* End of ROM */ -/* MII phy registers */ -#define PHY_CTRL 0x00 -#define PHY_STAT 0x01 -#define PHY_ID0 0x02 -#define PHY_ID1 0x03 -#define PHY_ADV 0x04 -#define PHY_LPA 0x05 - -#define PHY_CTRL_SPD2 0x0040 /* Gigabit enable? (bcm5411) */ -#define PHY_CTRL_FDPLX 0x0100 /* Full duplex */ -#define PHY_CTRL_ISO 0x0400 /* Isloate MII from PHY */ -#define PHY_CTRL_ANRES 0x0200 /* Auto-negotiation restart */ -#define PHY_CTRL_ANENAB 0x1000 /* Auto-negotiation enable */ -#define PHY_CTRL_SPD100 0x2000 /* Select 100Mbps */ -#define PHY_CTRL_RST 0x8000 /* Reset PHY */ - -#define PHY_STAT_LSTAT 0x0004 /* Link status */ -#define PHY_STAT_ANEGC 0x0020 /* Auto-negotiation complete */ - -#define PHY_ADV_10HALF 0x0020 -#define PHY_ADV_10FULL 0x0040 -#define PHY_ADV_100HALF 0x0080 -#define PHY_ADV_100FULL 0x0100 - -#define PHY_LPA_10HALF 0x0020 -#define PHY_LPA_10FULL 0x0040 -#define PHY_LPA_100HALF 0x0080 -#define PHY_LPA_100FULL 0x0100 -#define PHY_LPA_PAUSE 0x0400 -#define PHY_LPA_FAULT 0x2000 +/* MII definitions missing from mii.h */ + +#define BMCR_SPD2 0x0040 /* Gigabit enable? (bcm5411) */ +#define LPA_PAUSE 0x0400 /* More PHY registers (specific to Broadcom models) */ /* MII BCM5201 MULTIPHY interrupt register */ -#define PHY_BCM5201_INTERRUPT 0x1A -#define PHY_BCM5201_INTERRUPT_INTENABLE 0x4000 +#define MII_BCM5201_INTERRUPT 0x1A +#define MII_BCM5201_INTERRUPT_INTENABLE 0x4000 -#define PHY_BCM5201_AUXMODE2 0x1B -#define PHY_BCM5201_AUXMODE2_LOWPOWER 0x0008 +#define MII_BCM5201_AUXMODE2 0x1B +#define MII_BCM5201_AUXMODE2_LOWPOWER 0x0008 -#define PHY_BCM5201_MULTIPHY 0x1E +#define MII_BCM5201_MULTIPHY 0x1E /* MII BCM5201 MULTIPHY register bits */ -#define PHY_BCM5201_MULTIPHY_SERIALMODE 0x0002 -#define PHY_BCM5201_MULTIPHY_SUPERISOLATE 0x0008 +#define MII_BCM5201_MULTIPHY_SERIALMODE 0x0002 +#define MII_BCM5201_MULTIPHY_SUPERISOLATE 0x0008 /* MII BCM5400 1000-BASET Control register */ -#define PHY_BCM5400_GB_CONTROL 0x09 -#define PHY_BCM5400_GB_CONTROL_FULLDUPLEXCAP 0x0200 +#define MII_BCM5400_GB_CONTROL 0x09 +#define MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP 0x0200 /* MII BCM5400 AUXCONTROL register */ -#define PHY_BCM5400_AUXCONTROL 0x18 -#define PHY_BCM5400_AUXCONTROL_PWR10BASET 0x0004 +#define MII_BCM5400_AUXCONTROL 0x18 +#define MII_BCM5400_AUXCONTROL_PWR10BASET 0x0004 /* MII BCM5400 AUXSTATUS register */ -#define PHY_BCM5400_AUXSTATUS 0x19 -#define PHY_BCM5400_AUXSTATUS_LINKMODE_MASK 0x0700 -#define PHY_BCM5400_AUXSTATUS_LINKMODE_SHIFT 8 +#define MII_BCM5400_AUXSTATUS 0x19 +#define MII_BCM5400_AUXSTATUS_LINKMODE_MASK 0x0700 +#define MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT 8 /* When it can, GEM internally caches 4 aligned TX descriptors * at a time, so that it can use full cacheline DMA reads. @@ -970,9 +944,12 @@ enum gem_phy_model { }; enum link_state { - aneg_wait, - force_wait, - aneg_up, + link_down = 0, /* No link, will retry */ + link_aneg, /* Autoneg in progress */ + link_force_try, /* Try Forced link speed */ + link_force_ret, /* Forced mode worked, retrying autoneg */ + link_force_ok, /* Stay in forced mode */ + link_up /* Link is up */ }; struct gem { @@ -985,6 +962,10 @@ struct gem { * (ie. not power managed) */ int hw_running; + int opened; + struct semaphore pm_sem; + struct tq_struct pm_task; + struct timer_list pm_timer; struct gem_init_block *init_block; @@ -1000,15 +981,23 @@ struct gem { int rx_pause_off; int rx_pause_on; int mii_phy_addr; - + int gigabit_capable; + + /* Autoneg & PHY control */ + int link_cntl; + int link_advertise; + int link_fcntl; + enum link_state lstate; + struct timer_list link_timer; + int timer_ticks; + int wake_on_lan; + struct tq_struct reset_task; + volatile int reset_task_pending; + /* Diagnostic counters and state. */ u64 pause_entered; u16 pause_last_time_recvd; - struct timer_list link_timer; - int timer_ticks; - enum link_state lstate; - dma_addr_t gblock_dvma; struct pci_dev *pdev; struct net_device *dev; |