aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordavem <davem>2001-11-29 03:57:33 +0000
committerdavem <davem>2001-11-29 03:57:33 +0000
commitf88d4da353eeb4a4a1f76289325d984dd16048c0 (patch)
tree5fd6ea386adb166f5f2c8cd321e985caf7c4c790
parentd48c56b644a9d4bd7d83a9e997afb4d85241d92c (diff)
downloadnetdev-vger-cvs-f88d4da353eeb4a4a1f76289325d984dd16048c0.tar.gz
PPC support updates from Banjamin Herrenscmidt
-rw-r--r--drivers/net/sungem.c1040
-rw-r--r--drivers/net/sungem.h95
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(&regs, useraddr, sizeof(regs)))
+ return -EFAULT;
+
+ if (regs.len > SUNGEM_NREGS) {
+ regs.len = SUNGEM_NREGS;
+ }
+ regs.version = 0;
+ if (copy_to_user(useraddr, &regs, 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;