This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / net / sungem.c
index 5cd50fd..38e9496 100644 (file)
@@ -3,32 +3,16 @@
  *
  * 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.
+ * Support for Apple GMAC and assorted PHYs by
+ * Benjamin Herrenscmidt (benh@kernel.crashing.org)
  *
  * NAPI and NETPOLL support
  * (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
  * 
  * TODO: 
- *  - Now that the driver was significantly simplified, I need to rework
- *    the locking. I'm sure we don't need _2_ spinlocks, and we probably
- *    can avoid taking most of them for so long period of time (and schedule
- *    instead). The main issues at this point are caused by the netdev layer
- *    though:
- *    
- *    gem_change_mtu() and gem_set_multicast() are called with a read_lock()
- *    help by net/core/dev.c, thus they can't schedule. That means they can't
- *    call netif_poll_disable() neither, thus force gem_poll() to keep a spinlock
- *    where it could have been dropped. change_mtu especially would love also to
- *    be able to msleep instead of horrid locked delays when resetting the HW,
- *    but that read_lock() makes it impossible, unless I defer it's action to
- *    the reset task, which means it'll be asynchronous (won't take effect until
- *    the system schedules a bit).
- *
- *    Also, it would probably be possible to also remove most of the long-life
- *    locking in open/resume code path (gem_reinit_chip) by beeing more careful
- *    about when we can start taking interrupts or get xmit() called...
+ *  - Get rid of all those nasty mdelay's and replace them
+ * with schedule_timeout.
+ *  - Implement WOL
  */
 
 #include <linux/module.h>
@@ -125,8 +109,6 @@ static struct pci_device_id gem_pci_tbl[] = {
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
        { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
-       { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM,
-         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
        {0, }
 };
 
@@ -214,33 +196,6 @@ static inline void gem_disable_ints(struct gem *gp)
        writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
 }
 
-static void gem_get_cell(struct gem *gp)
-{
-       BUG_ON(gp->cell_enabled < 0);
-       gp->cell_enabled++;
-#ifdef CONFIG_PPC_PMAC
-       if (gp->cell_enabled == 1) {
-               mb();
-               pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
-               udelay(10);
-       }
-#endif /* CONFIG_PPC_PMAC */
-}
-
-/* Turn off the chip's clock */
-static void gem_put_cell(struct gem *gp)
-{
-       BUG_ON(gp->cell_enabled <= 0);
-       gp->cell_enabled--;
-#ifdef CONFIG_PPC_PMAC
-       if (gp->cell_enabled == 0) {
-               mb();
-               pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
-               udelay(10);
-       }
-#endif /* CONFIG_PPC_PMAC */
-}
-
 static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
 {
        if (netif_msg_intr(gp))
@@ -364,19 +319,7 @@ static int gem_rxmac_reset(struct gem *gp)
        u64 desc_dma;
        u32 val;
 
-       /* First, reset & disable MAC RX. */
-       writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
-       for (limit = 0; limit < 5000; limit++) {
-               if (!(readl(gp->regs + MAC_RXRST) & MAC_RXRST_CMD))
-                       break;
-               udelay(10);
-       }
-       if (limit == 5000) {
-               printk(KERN_ERR "%s: RX MAC will not reset, resetting whole "
-                       "chip.\n", dev->name);
-               return 1;
-       }
-
+       /* First, reset MAC RX. */
        writel(gp->mac_rx_cfg & ~MAC_RXCFG_ENAB,
               gp->regs + MAC_RXCFG);
        for (limit = 0; limit < 5000; limit++) {
@@ -654,7 +597,7 @@ static int gem_abnormal_irq(struct net_device *dev, struct gem *gp, u32 gem_stat
        return 0;
 
 do_reset:
-       gp->reset_task_pending = 1;
+       gp->reset_task_pending = 2;
        schedule_work(&gp->reset_task);
 
        return 1;
@@ -880,9 +823,6 @@ static int gem_poll(struct net_device *dev, int *budget)
        struct gem *gp = dev->priv;
        unsigned long flags;
 
-       /*
-        * NAPI locking nightmare: See comment at head of driver 
-        */
        spin_lock_irqsave(&gp->lock, flags);
 
        do {
@@ -934,11 +874,8 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        struct gem *gp = dev->priv;
        unsigned long flags;
 
-       /* Swallow interrupts when shutting the chip down, though
-        * that shouldn't happen, we should have done free_irq() at
-        * this point...
-        */
-       if (!gp->running)
+       /* Swallow interrupts when shutting the chip down */
+       if (!gp->hw_running)
                return IRQ_HANDLED;
 
        spin_lock_irqsave(&gp->lock, flags);
@@ -979,7 +916,7 @@ static void gem_tx_timeout(struct net_device *dev)
        struct gem *gp = dev->priv;
 
        printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
-       if (!gp->running) {
+       if (!gp->hw_running) {
                printk("%s: hrm.. hw not running !\n", dev->name);
                return;
        }
@@ -997,7 +934,7 @@ static void gem_tx_timeout(struct net_device *dev)
        spin_lock_irq(&gp->lock);
        spin_lock(&gp->tx_lock);
 
-       gp->reset_task_pending = 1;
+       gp->reset_task_pending = 2;
        schedule_work(&gp->reset_task);
 
        spin_unlock(&gp->tx_lock);
@@ -1038,11 +975,6 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev)
                local_irq_restore(flags);
                return NETDEV_TX_LOCKED;
        }
-       /* We raced with gem_do_stop() */
-       if (!gp->running) {
-               spin_unlock_irqrestore(&gp->tx_lock, flags);
-               return NETDEV_TX_BUSY;
-       }
 
        /* This is a hard error, log it. */
        if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) {
@@ -1141,10 +1073,46 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return NETDEV_TX_OK;
 }
 
+/* Jumbo-grams don't seem to work :-( */
+#define GEM_MIN_MTU    68
+#if 1
+#define GEM_MAX_MTU    1500
+#else
+#define GEM_MAX_MTU    9000
+#endif
+
+static int gem_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct gem *gp = dev->priv;
+
+       if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
+               return -EINVAL;
+
+       if (!netif_running(dev) || !netif_device_present(dev)) {
+               /* We'll just catch it later when the
+                * device is up'd or resumed.
+                */
+               dev->mtu = new_mtu;
+               return 0;
+       }
+
+       spin_lock_irq(&gp->lock);
+       spin_lock(&gp->tx_lock);
+       dev->mtu = new_mtu;
+       gp->reset_task_pending = 1;
+       schedule_work(&gp->reset_task);
+       spin_unlock(&gp->tx_lock);
+       spin_unlock_irq(&gp->lock);
+
+       flush_scheduled_work();
+
+       return 0;
+}
+
 #define STOP_TRIES 32
 
 /* Must be invoked under gp->lock and gp->tx_lock. */
-static void gem_reset(struct gem *gp)
+static void gem_stop(struct gem *gp)
 {
        int limit;
        u32 val;
@@ -1172,7 +1140,7 @@ static void gem_reset(struct gem *gp)
 /* Must be invoked under gp->lock and gp->tx_lock. */
 static void gem_start_dma(struct gem *gp)
 {
-       u32 val;
+       unsigned long val;
        
        /* We are ready to rock, turn everything on. */
        val = readl(gp->regs + TXDMA_CFG);
@@ -1187,31 +1155,10 @@ static void gem_start_dma(struct gem *gp)
        (void) readl(gp->regs + MAC_RXCFG);
        udelay(100);
 
-       gem_enable_ints(gp);
+       writel(GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
 
        writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
-}
-
-/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be
- * actually stopped before about 4ms tho ...
- */
-static void gem_stop_dma(struct gem *gp)
-{
-       u32 val;
-
-       /* We are done rocking, turn everything off. */
-       val = readl(gp->regs + TXDMA_CFG);
-       writel(val & ~TXDMA_CFG_ENABLE, gp->regs + TXDMA_CFG);
-       val = readl(gp->regs + RXDMA_CFG);
-       writel(val & ~RXDMA_CFG_ENABLE, gp->regs + RXDMA_CFG);
-       val = readl(gp->regs + MAC_TXCFG);
-       writel(val & ~MAC_TXCFG_ENAB, gp->regs + MAC_TXCFG);
-       val = readl(gp->regs + MAC_RXCFG);
-       writel(val & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
-
-       (void) readl(gp->regs + MAC_RXCFG);
 
-       /* Need to wait a bit ... done by the caller */
 }
 
 
@@ -1272,10 +1219,10 @@ start_aneg:
        if (speed == 0)
                speed = SPEED_10;
        
-       /* If we are asleep, we don't try to actually setup the PHY, we
+       /* If HW is down, we don't try to actually setup the PHY, we
         * just store the settings
         */
-       if (gp->asleep) {
+       if (!gp->hw_running) {
                gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
                gp->phy_mii.speed = speed;
                gp->phy_mii.duplex = duplex;
@@ -1332,9 +1279,6 @@ static int gem_set_link_modes(struct gem *gp)
                printk(KERN_INFO "%s: Link is up at %d Mbps, %s-duplex.\n",
                        gp->dev->name, speed, (full_duplex ? "full" : "half"));
 
-       if (!gp->running)
-               return 0;
-
        val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
        if (full_duplex) {
                val |= (MAC_TXCFG_ICS | MAC_TXCFG_ICOLL);
@@ -1461,19 +1405,48 @@ static int gem_mdio_link_not_up(struct gem *gp)
        }
 }
 
+static void gem_init_rings(struct gem *);
+static void gem_init_hw(struct gem *, int);
+
+static void gem_reset_task(void *data)
+{
+       struct gem *gp = (struct gem *) data;
+
+       netif_poll_disable(gp->dev);
+       spin_lock_irq(&gp->lock);
+       spin_lock(&gp->tx_lock);
+
+       if (gp->hw_running && gp->opened) {
+               netif_stop_queue(gp->dev);
+
+               /* Reset the chip & rings */
+               gem_stop(gp);
+               gem_init_rings(gp);
+
+               gem_init_hw(gp,
+                           (gp->reset_task_pending == 2));
+
+               netif_wake_queue(gp->dev);
+       }
+       gp->reset_task_pending = 0;
+
+       spin_unlock(&gp->tx_lock);
+       spin_unlock_irq(&gp->lock);
+       netif_poll_enable(gp->dev);
+}
+
 static void gem_link_timer(unsigned long data)
 {
        struct gem *gp = (struct gem *) data;
        int restart_aneg = 0;
                
-       if (gp->asleep)
+       if (!gp->hw_running)
                return;
 
        spin_lock_irq(&gp->lock);
        spin_lock(&gp->tx_lock);
-       gem_get_cell(gp);
 
-       /* If the reset task is still pending, we just
+       /* If the link of task is still pending, we just
         * reschedule the link timer
         */
        if (gp->reset_task_pending)
@@ -1489,7 +1462,8 @@ static void gem_link_timer(unsigned long data)
                if ((val & PCS_MIISTAT_LS) != 0) {
                        gp->lstate = link_up;
                        netif_carrier_on(gp->dev);
-                       (void)gem_set_link_modes(gp);
+                       if (gp->opened)
+                               (void)gem_set_link_modes(gp);
                }
                goto restart;
        }
@@ -1510,7 +1484,7 @@ static void gem_link_timer(unsigned long data)
                } else if (gp->lstate != link_up) {
                        gp->lstate = link_up;
                        netif_carrier_on(gp->dev);
-                       if (gem_set_link_modes(gp))
+                       if (gp->opened && gem_set_link_modes(gp))
                                restart_aneg = 1;
                }
        } else {
@@ -1523,7 +1497,7 @@ static void gem_link_timer(unsigned long data)
                                printk(KERN_INFO "%s: Link down\n",
                                        gp->dev->name);
                        netif_carrier_off(gp->dev);
-                       gp->reset_task_pending = 1;
+                       gp->reset_task_pending = 2;
                        schedule_work(&gp->reset_task);
                        restart_aneg = 1;
                } else if (++gp->timer_ticks > 10) {
@@ -1540,7 +1514,6 @@ static void gem_link_timer(unsigned long data)
 restart:
        mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
 out_unlock:
-       gem_put_cell(gp);
        spin_unlock(&gp->tx_lock);
        spin_unlock_irq(&gp->lock);
 }
@@ -1646,40 +1619,59 @@ static void gem_init_rings(struct gem *gp)
        wmb();
 }
 
-/* Init PHY interface and start link poll state machine */
+/* Must be invoked under gp->lock and gp->tx_lock. */
 static void gem_init_phy(struct gem *gp)
 {
        u32 mifcfg;
-
+       
        /* Revert MIF CFG setting done on stop_phy */
        mifcfg = readl(gp->regs + MIF_CFG);
        mifcfg &= ~MIF_CFG_BBMODE;
        writel(mifcfg, gp->regs + MIF_CFG);
        
+#ifdef CONFIG_PPC_PMAC
        if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
-               int i;
+               int i, j;
 
                /* Those delay sucks, the HW seem to love them though, I'll
                 * serisouly consider breaking some locks here to be able
                 * to schedule instead
                 */
-               for (i = 0; i < 3; i++) {
-#ifdef CONFIG_PPC_PMAC
-                       pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
-                       msleep(20);
-#endif
+               pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
+               for (j = 0; j < 3; j++) {
                        /* Some PHYs used by apple have problem getting back to us,
-                        * we do an additional reset here
+                        * we _know_ it's actually at addr 0 or 1, that's a hack, but
+                        * it helps to do that reset now. I suspect some motherboards
+                        * don't wire the PHY reset line properly, thus the PHY doesn't
+                        * come back with the above pmac_call_feature.
                         */
+                       gp->mii_phy_addr = 0;
                        phy_write(gp, MII_BMCR, BMCR_RESET);
-                       msleep(20);
-                       if (phy_read(gp, MII_BMCR) != 0xffff)
+                       gp->mii_phy_addr = 1;
+                       phy_write(gp, MII_BMCR, BMCR_RESET);
+                       /* We should probably break some locks here and schedule... */
+                       mdelay(10);
+                       
+                       /* On K2, we only probe the internal PHY at address 1, other
+                        * addresses tend to return garbage.
+                        */
+                       if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
                                break;
-                       if (i == 2)
+
+                       for (i = 0; i < 32; i++) {
+                               gp->mii_phy_addr = i;
+                               if (phy_read(gp, MII_BMCR) != 0xffff)
+                                       break;
+                       }
+                       if (i == 32) {
                                printk(KERN_WARNING "%s: GMAC PHY not responding !\n",
                                       gp->dev->name);
+                               gp->mii_phy_addr = 0;
+                       } else
+                               break;
                }
        }
+#endif /* CONFIG_PPC_PMAC */
 
        if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
            gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) {
@@ -1763,16 +1755,6 @@ static void gem_init_phy(struct gem *gp)
                        val |= PCS_SCTRL_LOOP;
                writel(val, gp->regs + PCS_SCTRL);
        }
-
-       /* Default aneg parameters */
-       gp->timer_ticks = 0;
-       gp->lstate = link_down;
-       netif_carrier_off(gp->dev);
-
-       /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
-       spin_lock_irq(&gp->lock);
-       gem_begin_auto_negotiation(gp, NULL);
-       spin_unlock_irq(&gp->lock);
 }
 
 /* Must be invoked under gp->lock and gp->tx_lock. */
@@ -1814,7 +1796,8 @@ static void gem_init_dma(struct gem *gp)
 }
 
 /* Must be invoked under gp->lock and gp->tx_lock. */
-static u32 gem_setup_multicast(struct gem *gp)
+static u32
+gem_setup_multicast(struct gem *gp)
 {
        u32 rxcfg = 0;
        int i;
@@ -1931,11 +1914,6 @@ static void gem_init_mac(struct gem *gp)
         * make no use of those events other than to record them.
         */
        writel(0xffffffff, gp->regs + MAC_MCMASK);
-
-       /* Don't enable GEM's WOL in normal operations
-        */
-       if (gp->has_wol)
-               writel(0, gp->regs + WOL_WAKECSR);
 }
 
 /* Must be invoked under gp->lock and gp->tx_lock. */
@@ -1997,23 +1975,6 @@ static int gem_check_invariants(struct gem *gp)
                gp->tx_fifo_sz = readl(gp->regs + TXDMA_FSZ) * 64;
                gp->rx_fifo_sz = readl(gp->regs + RXDMA_FSZ) * 64;
                gp->swrst_base = 0;
-
-               mif_cfg = readl(gp->regs + MIF_CFG);
-               mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
-               mif_cfg |= MIF_CFG_MDI0;
-               writel(mif_cfg, gp->regs + MIF_CFG);
-               writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
-               writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
-
-               /* We hard-code the PHY address so we can properly bring it out of
-                * reset later on, we can't really probe it at this point, though
-                * that isn't an issue.
-                */
-               if (gp->pdev->device == PCI_DEVICE_ID_APPLE_K2_GMAC)
-                       gp->mii_phy_addr = 1;
-               else
-                       gp->mii_phy_addr = 0;
-
                return 0;
        }
 
@@ -2092,28 +2053,68 @@ static int gem_check_invariants(struct gem *gp)
 }
 
 /* Must be invoked under gp->lock and gp->tx_lock. */
-static void gem_reinit_chip(struct gem *gp)
+static void gem_init_hw(struct gem *gp, int restart_link)
 {
-       /* Reset the chip */
-       gem_reset(gp);
+       /* On Apple's gmac, I initialize the PHY only after
+        * setting up the chip. It appears the gigabit PHYs
+        * don't quite like beeing talked to on the GII when
+        * the chip is not running, I suspect it might not
+        * be clocked at that point. --BenH
+        */
+       if (restart_link)
+               gem_init_phy(gp);
+       gem_init_pause_thresholds(gp);
+       gem_init_dma(gp);
+       gem_init_mac(gp);
 
-       /* Make sure ints are disabled */
-       gem_disable_ints(gp);
+       if (restart_link) {
+               /* Default aneg parameters */
+               gp->timer_ticks = 0;
+               gp->lstate = link_down;
+               netif_carrier_off(gp->dev);
 
-       /* Allocate & setup ring buffers */
-       gem_init_rings(gp);
+               /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
+               gem_begin_auto_negotiation(gp, NULL);
+       } else {
+               if (gp->lstate == link_up) {
+                       netif_carrier_on(gp->dev);
+                       gem_set_link_modes(gp);
+               }
+       }
+}
 
-       /* Configure pause thresholds */
-       gem_init_pause_thresholds(gp);
+#ifdef CONFIG_PPC_PMAC
+/* Enable the chip's clock and make sure it's config space is
+ * setup properly. There appear to be no need to restore the
+ * base addresses.
+ */
+static void gem_apple_powerup(struct gem *gp)
+{
+       u32 mif_cfg;
 
-       /* Init DMA & MAC engines */
-       gem_init_dma(gp);
-       gem_init_mac(gp);
+       mb();
+       pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
+
+       udelay(3);
+       
+       mif_cfg = readl(gp->regs + MIF_CFG);
+       mif_cfg &= ~(MIF_CFG_PSELECT|MIF_CFG_POLL|MIF_CFG_BBMODE|MIF_CFG_MDI1);
+       mif_cfg |= MIF_CFG_MDI0;
+       writel(mif_cfg, gp->regs + MIF_CFG);
+       writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
+       writel(MAC_XIFCFG_OE, gp->regs + MAC_XIFCFG);
+}
+
+/* Turn off the chip's clock */
+static void gem_apple_powerdown(struct gem *gp)
+{
+       pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 0);
 }
 
+#endif /* CONFIG_PPC_PMAC */
 
 /* Must be invoked with no lock held. */
-static void gem_stop_phy(struct gem *gp, int wol)
+static void gem_stop_phy(struct gem *gp)
 {
        u32 mifcfg;
        unsigned long flags;
@@ -2130,22 +2131,8 @@ static void gem_stop_phy(struct gem *gp, int wol)
        mifcfg &= ~MIF_CFG_POLL;
        writel(mifcfg, gp->regs + MIF_CFG);
 
-       if (wol && gp->has_wol) {
-               unsigned char *e = &gp->dev->dev_addr[0];
-               u32 csr;
-
-               /* Setup wake-on-lan for MAGIC packet */
-               writel(MAC_RXCFG_HFE | MAC_RXCFG_SFCS | MAC_RXCFG_ENAB,
-                      gp->regs + MAC_RXCFG);   
-               writel((e[4] << 8) | e[5], gp->regs + WOL_MATCH0);
-               writel((e[2] << 8) | e[3], gp->regs + WOL_MATCH1);
-               writel((e[0] << 8) | e[1], gp->regs + WOL_MATCH2);
-
-               writel(WOL_MCOUNT_N | WOL_MCOUNT_M, gp->regs + WOL_MCOUNT);
-               csr = WOL_WAKECSR_ENABLE;
-               if ((readl(gp->regs + MAC_XIFCFG) & MAC_XIFCFG_GMII) == 0)
-                       csr |= WOL_WAKECSR_MII;
-               writel(csr, gp->regs + WOL_WAKECSR);
+       if (gp->wake_on_lan) {
+               /* Setup wake-on-lan */
        } else {
                writel(0, gp->regs + MAC_RXCFG);
                (void)readl(gp->regs + MAC_RXCFG);
@@ -2161,20 +2148,20 @@ static void gem_stop_phy(struct gem *gp, int wol)
        writel(0, gp->regs + TXDMA_CFG);
        writel(0, gp->regs + RXDMA_CFG);
 
-       if (!wol) {
+       if (!gp->wake_on_lan) {
                spin_lock_irqsave(&gp->lock, flags);
                spin_lock(&gp->tx_lock);
-               gem_reset(gp);
+               gem_stop(gp);
                writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
                writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
                spin_unlock(&gp->tx_lock);
                spin_unlock_irqrestore(&gp->lock, flags);
+       }
 
-               /* No need to take the lock here */
-
-               if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
-                       gp->phy_mii.def->ops->suspend(&gp->phy_mii);
+       if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
+               gp->phy_mii.def->ops->suspend(&gp->phy_mii, 0 /* wake on lan options */);
 
+       if (!gp->wake_on_lan) {
                /* According to Apple, we must set the MDIO pins to this begnign
                 * state or we may 1) eat more current, 2) damage some PHYs
                 */
@@ -2187,160 +2174,181 @@ static void gem_stop_phy(struct gem *gp, int wol)
        }
 }
 
-
-static int gem_do_start(struct net_device *dev)
+/* Shut down the chip, must be called with pm_sem held.  */
+static void gem_shutdown(struct gem *gp)
 {
-       struct gem *gp = dev->priv;
-       unsigned long flags;
-
-       spin_lock_irqsave(&gp->lock, flags);
-       spin_lock(&gp->tx_lock);
-
-       /* Enable the cell */
-       gem_get_cell(gp);
-
-       /* Init & setup chip hardware */
-       gem_reinit_chip(gp);
-
-       gp->running = 1;
-
-       if (gp->lstate == link_up) {
-               netif_carrier_on(gp->dev);
-               gem_set_link_modes(gp);
-       }
+       /* Make us not-running to avoid timers respawning
+        * and swallow irqs 
+        */
+       gp->hw_running = 0;
+       wmb();
 
-       netif_wake_queue(gp->dev);
+       /* Stop the link timer */
+       del_timer_sync(&gp->link_timer);
 
-       spin_unlock(&gp->tx_lock);
-       spin_unlock_irqrestore(&gp->lock, flags);
+       /* Stop the reset task */
+       while (gp->reset_task_pending)
+               yield();
+       
+       /* Actually stop the chip */
+       if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
+               gem_stop_phy(gp);
 
-       if (request_irq(gp->pdev->irq, gem_interrupt,
-                                  SA_SHIRQ, dev->name, (void *)dev)) {
-               printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
+#ifdef CONFIG_PPC_PMAC
+               /* Power down the chip */
+               gem_apple_powerdown(gp);
+#endif /* CONFIG_PPC_PMAC */
+       } else{
+               unsigned long flags;
 
                spin_lock_irqsave(&gp->lock, flags);
                spin_lock(&gp->tx_lock);
-
-               gp->running =  0;
-               gem_reset(gp);
-               gem_clean_rings(gp);
-               gem_put_cell(gp);
-               
+               gem_stop(gp);
                spin_unlock(&gp->tx_lock);
                spin_unlock_irqrestore(&gp->lock, flags);
-
-               return -EAGAIN;
        }
-
-       return 0;
 }
 
-static void gem_do_stop(struct net_device *dev, int wol)
+static void gem_pm_task(void *data)
 {
-       struct gem *gp = dev->priv;
-       unsigned long flags;
-
-       spin_lock_irqsave(&gp->lock, flags);
-       spin_lock(&gp->tx_lock);
-
-       gp->running = 0;
-
-       /* Stop netif queue */
-       netif_stop_queue(dev);
+       struct gem *gp = (struct gem *) data;
 
-       /* Make sure ints are disabled */
-       gem_disable_ints(gp);
+       /* 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;
 
-       /* We can drop the lock now */
-       spin_unlock(&gp->tx_lock);
-       spin_unlock_irqrestore(&gp->lock, flags);
+       /* Driver was re-opened or already shut down */
+       if (gp->opened || !gp->hw_running) {
+               up(&gp->pm_sem);
+               return;
+       }
 
-       /* If we are going to sleep with WOL */
-       gem_stop_dma(gp);
-       msleep(10);
-       if (!wol)
-               gem_reset(gp);
-       msleep(10);
+       gem_shutdown(gp);
 
-       /* Get rid of rings */
-       gem_clean_rings(gp);
+       up(&gp->pm_sem);
+}
 
-       /* No irq needed anymore */
-       free_irq(gp->pdev->irq, (void *) dev);
+static void gem_pm_timer(unsigned long data)
+{
+       struct gem *gp = (struct gem *) data;
 
-       /* Cell not needed neither if no WOL */
-       if (!wol) {
-               spin_lock_irqsave(&gp->lock, flags);
-               gem_put_cell(gp);
-               spin_unlock_irqrestore(&gp->lock, flags);
-       }
+       schedule_work(&gp->pm_task);
 }
 
-static void gem_reset_task(void *data)
+static int gem_open(struct net_device *dev)
 {
-       struct gem *gp = (struct gem *) data;
+       struct gem *gp = dev->priv;
+       int hw_was_up;
 
        down(&gp->pm_sem);
 
-       netif_poll_disable(gp->dev);
-
-       spin_lock_irq(&gp->lock);
-       spin_lock(&gp->tx_lock);
+       hw_was_up = gp->hw_running;
 
-       if (gp->running == 0)
-               goto not_running;
+       /* Stop the PM timer/task */
+       del_timer(&gp->pm_timer);
+       flush_scheduled_work();
 
-       if (gp->running) {
-               netif_stop_queue(gp->dev);
+       /* The power-management semaphore protects the hw_running
+        * etc. state so it is safe to do this bit without gp->lock
+        */
+       if (!gp->hw_running) {
+#ifdef CONFIG_PPC_PMAC
+               /* 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_PPC_PMAC */
 
-               /* Reset the chip & rings */
-               gem_reinit_chip(gp);
-               if (gp->lstate == link_up)
-                       gem_set_link_modes(gp);
-               netif_wake_queue(gp->dev);
+               /* Reset the chip */
+               spin_lock_irq(&gp->lock);
+               spin_lock(&gp->tx_lock);
+               gem_stop(gp);
+               spin_unlock(&gp->tx_lock);
+               spin_unlock_irq(&gp->lock);
+
+               gp->hw_running = 1;
        }
- not_running:
-       gp->reset_task_pending = 0;
 
-       spin_unlock(&gp->tx_lock);
-       spin_unlock_irq(&gp->lock);
+       /* We can now request the interrupt as we know it's masked
+        * on the controller
+        */
+       if (request_irq(gp->pdev->irq, gem_interrupt,
+                       SA_SHIRQ, dev->name, (void *)dev)) {
+               printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
 
-       netif_poll_enable(gp->dev);
+               spin_lock_irq(&gp->lock);
+               spin_lock(&gp->tx_lock);
+#ifdef CONFIG_PPC_PMAC
+               if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
+                       gem_apple_powerdown(gp);
+#endif /* CONFIG_PPC_PMAC */
+               /* 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);
+               spin_unlock(&gp->tx_lock);
+               spin_unlock_irq(&gp->lock);
 
-       up(&gp->pm_sem);
-}
+               return -EAGAIN;
+       }
 
+               spin_lock_irq(&gp->lock);
+       spin_lock(&gp->tx_lock);
 
-static int gem_open(struct net_device *dev)
-{
-       struct gem *gp = dev->priv;
-       int rc = 0;
+       /* Allocate & setup ring buffers */
+       gem_init_rings(gp);
 
-       down(&gp->pm_sem);
+       /* Init & setup chip hardware */
+       gem_init_hw(gp, !hw_was_up);
 
-       /* We need the cell enabled */
-       if (!gp->asleep)
-               rc = gem_do_start(dev);
-       gp->opened = (rc == 0);
+       gp->opened = 1;
+
+       spin_unlock(&gp->tx_lock);
+       spin_unlock_irq(&gp->lock);
 
        up(&gp->pm_sem);
 
-       return rc;
+       return 0;
 }
 
 static int gem_close(struct net_device *dev)
 {
        struct gem *gp = dev->priv;
 
+       /* Make sure we don't get distracted by suspend/resume */
+       down(&gp->pm_sem);
+
        /* Note: we don't need to call netif_poll_disable() here because
         * our caller (dev_close) already did it for us
         */
 
-       down(&gp->pm_sem);
+       /* Stop traffic, mark us closed */
+       spin_lock_irq(&gp->lock);
+       spin_lock(&gp->tx_lock);
 
        gp->opened = 0; 
-       if (!gp->asleep)
-               gem_do_stop(dev, 0);
+
+       netif_stop_queue(dev);
+
+       /* Stop chip */
+       gem_stop(gp);
+
+       /* Get rid of rings */
+       gem_clean_rings(gp);
+
+       /* Bye, the pm timer will finish the job */
+       free_irq(gp->pdev->irq, (void *) dev);
+
+       spin_unlock(&gp->tx_lock);
+       spin_unlock_irq(&gp->lock);
+
+       /* 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);
        
@@ -2348,66 +2356,49 @@ static int gem_close(struct net_device *dev)
 }
 
 #ifdef CONFIG_PM
-static int gem_suspend(struct pci_dev *pdev, pm_message_t state)
+static int gem_suspend(struct pci_dev *pdev, u32 state)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct gem *gp = dev->priv;
-       unsigned long flags;
-
-       down(&gp->pm_sem);
 
        netif_poll_disable(dev);
 
+       /* 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 && gp->opened) ? "enabled" : "disabled");
+              dev->name, gp->wake_on_lan ? "enabled" : "disabled");
        
-       /* Keep the cell enabled during the entire operation */
-       spin_lock_irqsave(&gp->lock, flags);
-       spin_lock(&gp->tx_lock);
-       gem_get_cell(gp);
-       spin_unlock(&gp->tx_lock);
-       spin_unlock_irqrestore(&gp->lock, flags);
-
-       /* If the driver is opened, we stop the MAC */
+       /* If the driver is opened, we stop the DMA */
        if (gp->opened) {
+               spin_lock_irq(&gp->lock);
+               spin_lock(&gp->tx_lock);
+
                /* Stop traffic, mark us closed */
                netif_device_detach(dev);
 
-               /* Switch off MAC, remember WOL setting */
-               gp->asleep_wol = gp->wake_on_lan;
-               gem_do_stop(dev, gp->asleep_wol);
-       } else
-               gp->asleep_wol = 0;
-
-       /* Mark us asleep */
-       gp->asleep = 1;
-       wmb();
+               /* Stop chip */
+               gem_stop(gp);
 
-       /* Stop the link timer */
-       del_timer_sync(&gp->link_timer);
-
-       /* Now we release the semaphore to not block the reset task who
-        * can take it too. We are marked asleep, so there will be no
-        * conflict here
-        */
-       up(&gp->pm_sem);
+               /* Get rid of ring buffers */
+               gem_clean_rings(gp);
 
-       /* Wait for a pending reset task to complete */
-       while (gp->reset_task_pending)
-               yield();
-       flush_scheduled_work();
+               spin_unlock(&gp->tx_lock);
+               spin_unlock_irq(&gp->lock);
 
-       /* Shut the PHY down eventually and setup WOL */
-       gem_stop_phy(gp, gp->asleep_wol);
+               if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
+                       disable_irq(gp->pdev->irq);
+       }
 
-       /* Make sure bus master is disabled */
-       pci_disable_device(gp->pdev);
+       if (gp->hw_running) {
+               /* Kill PM timer if any */
+               del_timer_sync(&gp->pm_timer);
+               flush_scheduled_work();
 
-       /* Release the cell, no need to take a lock at this point since
-        * nothing else can happen now
-        */
-       gem_put_cell(gp);
+               gem_shutdown(gp);
+       }
 
        return 0;
 }
@@ -2416,74 +2407,36 @@ static int gem_resume(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct gem *gp = dev->priv;
-       unsigned long flags;
 
        printk(KERN_INFO "%s: resuming\n", dev->name);
 
-       down(&gp->pm_sem);
-
-       /* Keep the cell enabled during the entire operation, no need to
-        * take a lock here tho since nothing else can happen while we are
-        * marked asleep
-        */
-       gem_get_cell(gp);
-
-       /* Make sure PCI access and bus master are enabled */
-       if (pci_enable_device(gp->pdev)) {
-               printk(KERN_ERR "%s: Can't re-enable chip !\n",
-                      dev->name);
-               /* Put cell and forget it for now, it will be considered as
-                * still asleep, a new sleep cycle may bring it back
-                */
-               gem_put_cell(gp);
-               up(&gp->pm_sem);
-               return 0;
-       }
-       pci_set_master(gp->pdev);
-
-       /* Reset everything */
-       gem_reset(gp);
-
-       /* Mark us woken up */
-       gp->asleep = 0;
-       wmb();
+       if (gp->opened) {
+#ifdef CONFIG_PPC_PMAC
+               /* 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_PPC_PMAC */
+               spin_lock_irq(&gp->lock);
+               spin_lock(&gp->tx_lock);
 
-       /* Bring the PHY back. Again, lock is useless at this point as
-        * nothing can be happening until we restart the whole thing
-        */
-       gem_init_phy(gp);
+               gem_stop(gp);
+               gp->hw_running = 1;
+               gem_init_rings(gp);
+               gem_init_hw(gp, 1);
 
-       /* If we were opened, bring everything back */
-       if (gp->opened) {
-               /* Restart MAC */
-               gem_do_start(dev);
+               spin_unlock(&gp->tx_lock);
+               spin_unlock_irq(&gp->lock);
 
-               /* Re-attach net device */
                netif_device_attach(dev);
-
+               if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
+                       enable_irq(gp->pdev->irq);
        }
-
-       spin_lock_irqsave(&gp->lock, flags);
-       spin_lock(&gp->tx_lock);
-
-       /* If we had WOL enabled, the cell clock was never turned off during
-        * sleep, so we end up beeing unbalanced. Fix that here
-        */
-       if (gp->asleep_wol)
-               gem_put_cell(gp);
-
-       /* This function doesn't need to hold the cell, it will be held if the
-        * driver is open by gem_do_start().
-        */
-       gem_put_cell(gp);
-
-       spin_unlock(&gp->tx_lock);
-       spin_unlock_irqrestore(&gp->lock, flags);
+       up(&gp->pm_sem);
 
        netif_poll_enable(dev);
        
-       up(&gp->pm_sem);
-
        return 0;
 }
 #endif /* CONFIG_PM */
@@ -2496,10 +2449,7 @@ static struct net_device_stats *gem_get_stats(struct net_device *dev)
        spin_lock_irq(&gp->lock);
        spin_lock(&gp->tx_lock);
 
-       /* I have seen this being called while the PM was in progress,
-        * so we shield against this
-        */
-       if (gp->running) {
+       if (gp->hw_running) {
                stats->rx_crc_errors += readl(gp->regs + MAC_FCSERR);
                writel(0, gp->regs + MAC_FCSERR);
 
@@ -2529,13 +2479,12 @@ static void gem_set_multicast(struct net_device *dev)
        u32 rxcfg, rxcfg_new;
        int limit = 10000;
        
-
+       if (!gp->hw_running)
+               return;
+               
        spin_lock_irq(&gp->lock);
        spin_lock(&gp->tx_lock);
 
-       if (!gp->running)
-               goto bail;
-
        netif_stop_queue(dev);
 
        rxcfg = readl(gp->regs + MAC_RXCFG);
@@ -2559,50 +2508,10 @@ static void gem_set_multicast(struct net_device *dev)
 
        netif_wake_queue(dev);
 
- bail:
        spin_unlock(&gp->tx_lock);
        spin_unlock_irq(&gp->lock);
 }
 
-/* Jumbo-grams don't seem to work :-( */
-#define GEM_MIN_MTU    68
-#if 1
-#define GEM_MAX_MTU    1500
-#else
-#define GEM_MAX_MTU    9000
-#endif
-
-static int gem_change_mtu(struct net_device *dev, int new_mtu)
-{
-       struct gem *gp = dev->priv;
-
-       if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
-               return -EINVAL;
-
-       if (!netif_running(dev) || !netif_device_present(dev)) {
-               /* We'll just catch it later when the
-                * device is up'd or resumed.
-                */
-               dev->mtu = new_mtu;
-               return 0;
-       }
-
-       down(&gp->pm_sem);
-       spin_lock_irq(&gp->lock);
-       spin_lock(&gp->tx_lock);
-       dev->mtu = new_mtu;
-       if (gp->running) {
-               gem_reinit_chip(gp);
-               if (gp->lstate == link_up)
-                       gem_set_link_modes(gp);
-       }
-       spin_unlock(&gp->tx_lock);
-       spin_unlock_irq(&gp->lock);
-       up(&gp->pm_sem);
-
-       return 0;
-}
-
 static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
        struct gem *gp = dev->priv;
@@ -2631,6 +2540,7 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 
                /* Return current PHY settings */
                spin_lock_irq(&gp->lock);
+               spin_lock(&gp->tx_lock);
                cmd->autoneg = gp->want_autoneg;
                cmd->speed = gp->phy_mii.speed;
                cmd->duplex = gp->phy_mii.duplex;                       
@@ -2642,6 +2552,7 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                 */
                if (cmd->advertising == 0)
                        cmd->advertising = cmd->supported;
+               spin_unlock(&gp->tx_lock);
                spin_unlock_irq(&gp->lock);
        } else { // XXX PCS ?
                cmd->supported =
@@ -2681,9 +2592,9 @@ static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
              
        /* Apply settings and restart link process. */
        spin_lock_irq(&gp->lock);
-       gem_get_cell(gp);
+       spin_lock(&gp->tx_lock);
        gem_begin_auto_negotiation(gp, cmd);
-       gem_put_cell(gp);
+       spin_unlock(&gp->tx_lock);
        spin_unlock_irq(&gp->lock);
 
        return 0;
@@ -2698,9 +2609,9 @@ static int gem_nway_reset(struct net_device *dev)
 
        /* Restart link process. */
        spin_lock_irq(&gp->lock);
-       gem_get_cell(gp);
+       spin_lock(&gp->tx_lock);
        gem_begin_auto_negotiation(gp, NULL);
-       gem_put_cell(gp);
+       spin_unlock(&gp->tx_lock);
        spin_unlock_irq(&gp->lock);
 
        return 0;
@@ -2717,37 +2628,7 @@ static void gem_set_msglevel(struct net_device *dev, u32 value)
        struct gem *gp = dev->priv;
        gp->msg_enable = value;
 }
-
-
-/* Add more when I understand how to program the chip */
-/* like WAKE_UCAST | WAKE_MCAST | WAKE_BCAST */
-
-#define WOL_SUPPORTED_MASK     (WAKE_MAGIC)
-
-static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
-       struct gem *gp = dev->priv;
-
-       /* Add more when I understand how to program the chip */
-       if (gp->has_wol) {
-               wol->supported = WOL_SUPPORTED_MASK;
-               wol->wolopts = gp->wake_on_lan;
-       } else {
-               wol->supported = 0;
-               wol->wolopts = 0;
-       }
-}
-
-static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
-       struct gem *gp = dev->priv;
-
-       if (!gp->has_wol)
-               return -EOPNOTSUPP;
-       gp->wake_on_lan = wol->wolopts & WOL_SUPPORTED_MASK;
-       return 0;
-}
-
+  
 static struct ethtool_ops gem_ethtool_ops = {
        .get_drvinfo            = gem_get_drvinfo,
        .get_link               = ethtool_op_get_link,
@@ -2756,8 +2637,6 @@ static struct ethtool_ops gem_ethtool_ops = {
        .nway_reset             = gem_nway_reset,
        .get_msglevel           = gem_get_msglevel,
        .set_msglevel           = gem_set_msglevel,
-       .get_wol                = gem_get_wol,
-       .set_wol                = gem_set_wol,
 };
 
 static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -2765,28 +2644,22 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        struct gem *gp = dev->priv;
        struct mii_ioctl_data *data = if_mii(ifr);
        int rc = -EOPNOTSUPP;
-       unsigned long flags;
-
+       
        /* Hold the PM semaphore while doing ioctl's or we may collide
-        * with power management.
+        * with open/close and power management and oops.
         */
        down(&gp->pm_sem);
-               
-       spin_lock_irqsave(&gp->lock, flags);
-       gem_get_cell(gp);
-       spin_unlock_irqrestore(&gp->lock, flags);
-
+       
        switch (cmd) {
        case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
                data->phy_id = gp->mii_phy_addr;
                /* Fallthrough... */
 
        case SIOCGMIIREG:               /* Read MII PHY register. */
-               if (!gp->running)
-                       rc = -EAGAIN;
+               if (!gp->hw_running)
+                       rc = -EIO;
                else {
-                       data->val_out = __phy_read(gp, data->phy_id & 0x1f,
-                                                  data->reg_num & 0x1f);
+                       data->val_out = __phy_read(gp, data->phy_id & 0x1f, data->reg_num & 0x1f);
                        rc = 0;
                }
                break;
@@ -2794,19 +2667,14 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        case SIOCSMIIREG:               /* Write MII PHY register. */
                if (!capable(CAP_NET_ADMIN))
                        rc = -EPERM;
-               else if (!gp->running)
-                       rc = -EAGAIN;
+               else if (!gp->hw_running)
+                       rc = -EIO;
                else {
-                       __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
-                                   data->val_in);
+                       __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
                        rc = 0;
                }
                break;
        };
-       
-       spin_lock_irqsave(&gp->lock, flags);
-       gem_put_cell(gp);
-       spin_unlock_irqrestore(&gp->lock, flags);
 
        up(&gp->pm_sem);
        
@@ -2911,47 +2779,6 @@ static int __devinit gem_get_device_address(struct gem *gp)
        return 0;
 }
 
-static void __devexit gem_remove_one(struct pci_dev *pdev)
-{
-       struct net_device *dev = pci_get_drvdata(pdev);
-
-       if (dev) {
-               struct gem *gp = dev->priv;
-
-               unregister_netdev(dev);
-
-               /* Stop the link timer */
-               del_timer_sync(&gp->link_timer);
-
-               /* We shouldn't need any locking here */
-               gem_get_cell(gp);
-
-               /* Wait for a pending reset task to complete */
-               while (gp->reset_task_pending)
-                       yield();
-               flush_scheduled_work();
-
-               /* Shut the PHY down */
-               gem_stop_phy(gp, 0);
-
-               gem_put_cell(gp);
-
-               /* Make sure bus master is disabled */
-               pci_disable_device(gp->pdev);
-
-               /* Free resources */
-               pci_free_consistent(pdev,
-                                   sizeof(struct gem_init_block),
-                                   gp->init_block,
-                                   gp->gblock_dvma);
-               iounmap(gp->regs);
-               pci_release_regions(pdev);
-               free_netdev(dev);
-
-               pci_set_drvdata(pdev, NULL);
-       }
-}
-
 static int __devinit gem_init_one(struct pci_dev *pdev,
                                  const struct pci_device_id *ent)
 {
@@ -3000,7 +2827,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
                }
                pci_using_dac = 0;
        }
-       
+
        gemreg_base = pci_resource_start(pdev, 0);
        gemreg_len = pci_resource_len(pdev, 0);
 
@@ -3043,6 +2870,11 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
        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_WORK(&gp->pm_task, gem_pm_task, gp);
        INIT_WORK(&gp->reset_task, gem_reset_task, gp);
        
        gp->lstate = link_down;
@@ -3057,22 +2889,20 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
                goto err_out_free_res;
        }
 
-       /* On Apple, we want a reference to the Open Firmware device-tree
-        * node. We use it for clock control.
+       /* 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_PPC_PMAC
        gp->of_node = pci_device_to_OF_node(pdev);
-#endif
-
-       /* Only Apple version supports WOL afaik */
        if (pdev->vendor == PCI_VENDOR_ID_APPLE)
-               gp->has_wol = 1;
-
-       /* Make sure cell is enabled */
-       gem_get_cell(gp);
-
-       /* Make sure everything is stopped and in init state */
-       gem_reset(gp);
+               gem_apple_powerup(gp);
+#endif
+       spin_lock_irq(&gp->lock);
+       spin_lock(&gp->tx_lock);
+       gem_stop(gp);
+       spin_unlock(&gp->tx_lock);
+       spin_unlock_irq(&gp->lock);
 
        /* Fill up the mii_phy structure (even if we won't use it) */
        gp->phy_mii.dev = dev;
@@ -3081,8 +2911,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
 
        /* By default, we start with autoneg */
        gp->want_autoneg = 1;
-
-       /* Check fifo sizes, PHY type, etc... */
+       
        if (gem_check_invariants(gp)) {
                err = -ENODEV;
                goto err_out_iounmap;
@@ -3122,19 +2951,6 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
        dev->poll_controller = gem_poll_controller;
 #endif
 
-       /* Set that now, in case PM kicks in now */
-       pci_set_drvdata(pdev, dev);
-
-       /* Detect & init PHY, start autoneg, we release the cell now
-        * too, it will be managed by whoever needs it
-        */
-       gem_init_phy(gp);
-
-       spin_lock_irq(&gp->lock);
-       gem_put_cell(gp);
-       spin_unlock_irq(&gp->lock);
-
-       /* Register with kernel */
        if (register_netdev(dev)) {
                printk(KERN_ERR PFX "Cannot register net device, "
                       "aborting.\n");
@@ -3149,22 +2965,48 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
                       i == 5 ? ' ' : ':');
        printk("\n");
 
+       /* Detect & init PHY, start autoneg */
+       spin_lock_irq(&gp->lock);
+       spin_lock(&gp->tx_lock);
+       gp->hw_running = 1;
+       gem_init_phy(gp);
+       gem_begin_auto_negotiation(gp, NULL);
+       spin_unlock(&gp->tx_lock);
+       spin_unlock_irq(&gp->lock);
+
        if (gp->phy_type == phy_mii_mdio0 ||
            gp->phy_type == phy_mii_mdio1)
                printk(KERN_INFO "%s: Found %s PHY\n", dev->name, 
                        gp->phy_mii.def ? gp->phy_mii.def->name : "no");
 
+       pci_set_drvdata(pdev, dev);
+
        /* GEM can do it all... */
        dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_LLTX;
        if (pci_using_dac)
                dev->features |= NETIF_F_HIGHDMA;
 
+       /* 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_free_consistent:
-       gem_remove_one(pdev);
+       pci_free_consistent(pdev,
+                           sizeof(struct gem_init_block),
+                           gp->init_block,
+                           gp->gblock_dvma);
+
 err_out_iounmap:
-       gem_put_cell(gp);
+       down(&gp->pm_sem);
+       /* Stop the PM timer & task */
+       del_timer_sync(&gp->pm_timer);
+       flush_scheduled_work();
+       if (gp->hw_running)
+               gem_shutdown(gp);
+       up(&gp->pm_sem);
+
        iounmap(gp->regs);
 
 err_out_free_res:
@@ -3178,6 +3020,34 @@ err_disable_device:
 
 }
 
+static void __devexit gem_remove_one(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+
+       if (dev) {
+               struct gem *gp = dev->priv;
+
+               unregister_netdev(dev);
+
+               down(&gp->pm_sem);
+               /* Stop the PM timer & task */
+               del_timer_sync(&gp->pm_timer);
+               flush_scheduled_work();
+               if (gp->hw_running)
+                       gem_shutdown(gp);
+               up(&gp->pm_sem);
+
+               pci_free_consistent(pdev,
+                                   sizeof(struct gem_init_block),
+                                   gp->init_block,
+                                   gp->gblock_dvma);
+               iounmap(gp->regs);
+               pci_release_regions(pdev);
+               free_netdev(dev);
+
+               pci_set_drvdata(pdev, NULL);
+       }
+}
 
 static struct pci_driver gem_driver = {
        .name           = GEM_MODULE_NAME,