Merge git://git.kernel.org/pub/scm/linux/kernel/git/bart/ide-2.6
[safe/jmp/linux-2.6] / drivers / net / gianfar.c
index fa78d68..83a5cb6 100644 (file)
@@ -105,6 +105,7 @@ const char gfar_driver_version[] = "1.3";
 
 static int gfar_enet_open(struct net_device *dev);
 static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void gfar_reset_task(struct work_struct *work);
 static void gfar_timeout(struct net_device *dev);
 static int gfar_close(struct net_device *dev);
 struct sk_buff *gfar_new_skb(struct net_device *dev);
@@ -134,6 +135,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l
 static void gfar_vlan_rx_register(struct net_device *netdev,
                                struct vlan_group *grp);
 void gfar_halt(struct net_device *dev);
+static void gfar_halt_nodisable(struct net_device *dev);
 void gfar_start(struct net_device *dev);
 static void gfar_clear_exact_match(struct net_device *dev);
 static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
@@ -159,7 +161,7 @@ static int gfar_probe(struct platform_device *pdev)
        struct gfar_private *priv = NULL;
        struct gianfar_platform_data *einfo;
        struct resource *r;
-       int err = 0;
+       int err = 0, irq;
        DECLARE_MAC_BUF(mac);
 
        einfo = (struct gianfar_platform_data *) pdev->dev.platform_data;
@@ -185,15 +187,25 @@ static int gfar_probe(struct platform_device *pdev)
 
        /* fill out IRQ fields */
        if (einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
-               priv->interruptTransmit = platform_get_irq_byname(pdev, "tx");
-               priv->interruptReceive = platform_get_irq_byname(pdev, "rx");
-               priv->interruptError = platform_get_irq_byname(pdev, "error");
-               if (priv->interruptTransmit < 0 || priv->interruptReceive < 0 || priv->interruptError < 0)
+               irq = platform_get_irq_byname(pdev, "tx");
+               if (irq < 0)
+                       goto regs_fail;
+               priv->interruptTransmit = irq;
+
+               irq = platform_get_irq_byname(pdev, "rx");
+               if (irq < 0)
                        goto regs_fail;
+               priv->interruptReceive = irq;
+
+               irq = platform_get_irq_byname(pdev, "error");
+               if (irq < 0)
+                       goto regs_fail;
+               priv->interruptError = irq;
        } else {
-               priv->interruptTransmit = platform_get_irq(pdev, 0);
-               if (priv->interruptTransmit < 0)
+               irq = platform_get_irq(pdev, 0);
+               if (irq < 0)
                        goto regs_fail;
+               priv->interruptTransmit = irq;
        }
 
        /* get a pointer to the register memory */
@@ -207,6 +219,8 @@ static int gfar_probe(struct platform_device *pdev)
 
        spin_lock_init(&priv->txlock);
        spin_lock_init(&priv->rxlock);
+       spin_lock_init(&priv->bflock);
+       INIT_WORK(&priv->reset_task, gfar_reset_task);
 
        platform_set_drvdata(pdev, dev);
 
@@ -335,6 +349,9 @@ static int gfar_probe(struct platform_device *pdev)
        /* Enable most messages by default */
        priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
 
+       /* Carrier starts down, phylib will bring it up */
+       netif_carrier_off(dev);
+
        err = register_netdev(dev);
 
        if (err) {
@@ -378,6 +395,99 @@ static int gfar_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int gfar_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned long flags;
+       u32 tempval;
+
+       int magic_packet = priv->wol_en &&
+               (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+
+       netif_device_detach(dev);
+
+       if (netif_running(dev)) {
+               spin_lock_irqsave(&priv->txlock, flags);
+               spin_lock(&priv->rxlock);
+
+               gfar_halt_nodisable(dev);
+
+               /* Disable Tx, and Rx if wake-on-LAN is disabled. */
+               tempval = gfar_read(&priv->regs->maccfg1);
+
+               tempval &= ~MACCFG1_TX_EN;
+
+               if (!magic_packet)
+                       tempval &= ~MACCFG1_RX_EN;
+
+               gfar_write(&priv->regs->maccfg1, tempval);
+
+               spin_unlock(&priv->rxlock);
+               spin_unlock_irqrestore(&priv->txlock, flags);
+
+               napi_disable(&priv->napi);
+
+               if (magic_packet) {
+                       /* Enable interrupt on Magic Packet */
+                       gfar_write(&priv->regs->imask, IMASK_MAG);
+
+                       /* Enable Magic Packet mode */
+                       tempval = gfar_read(&priv->regs->maccfg2);
+                       tempval |= MACCFG2_MPEN;
+                       gfar_write(&priv->regs->maccfg2, tempval);
+               } else {
+                       phy_stop(priv->phydev);
+               }
+       }
+
+       return 0;
+}
+
+static int gfar_resume(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned long flags;
+       u32 tempval;
+       int magic_packet = priv->wol_en &&
+               (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+
+       if (!netif_running(dev)) {
+               netif_device_attach(dev);
+               return 0;
+       }
+
+       if (!magic_packet && priv->phydev)
+               phy_start(priv->phydev);
+
+       /* Disable Magic Packet mode, in case something
+        * else woke us up.
+        */
+
+       spin_lock_irqsave(&priv->txlock, flags);
+       spin_lock(&priv->rxlock);
+
+       tempval = gfar_read(&priv->regs->maccfg2);
+       tempval &= ~MACCFG2_MPEN;
+       gfar_write(&priv->regs->maccfg2, tempval);
+
+       gfar_start(dev);
+
+       spin_unlock(&priv->rxlock);
+       spin_unlock_irqrestore(&priv->txlock, flags);
+
+       netif_device_attach(dev);
+
+       napi_enable(&priv->napi);
+
+       return 0;
+}
+#else
+#define gfar_suspend NULL
+#define gfar_resume NULL
+#endif
 
 /* Reads the controller's registers to determine what interface
  * connects it to the PHY.
@@ -476,6 +586,18 @@ static void gfar_configure_serdes(struct net_device *dev)
        struct gfar_mii __iomem *regs =
                        (void __iomem *)&priv->regs->gfar_mii_regs;
        int tbipa = gfar_read(&priv->regs->tbipa);
+       struct mii_bus *bus = gfar_get_miibus(priv);
+
+       if (bus)
+               mutex_lock(&bus->mdio_lock);
+
+       /* If the link is already up, we must already be ok, and don't need to
+        * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
+        * everything for us?  Resetting it takes the link down and requires
+        * several seconds for it to come back.
+        */
+       if (gfar_local_mdio_read(regs, tbipa, MII_BMSR) & BMSR_LSTATUS)
+               goto done;
 
        /* Single clk mode, mii mode off(for serdes communication) */
        gfar_local_mdio_write(regs, tbipa, MII_TBICON, TBICON_CLK_SELECT);
@@ -486,6 +608,10 @@ static void gfar_configure_serdes(struct net_device *dev)
 
        gfar_local_mdio_write(regs, tbipa, MII_BMCR, BMCR_ANENABLE |
                        BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000);
+
+       done:
+       if (bus)
+               mutex_unlock(&bus->mdio_lock);
 }
 
 static void init_registers(struct net_device *dev)
@@ -535,7 +661,7 @@ static void init_registers(struct net_device *dev)
 
 
 /* Halt the receive and transmit queues */
-void gfar_halt(struct net_device *dev)
+static void gfar_halt_nodisable(struct net_device *dev)
 {
        struct gfar_private *priv = netdev_priv(dev);
        struct gfar __iomem *regs = priv->regs;
@@ -558,6 +684,16 @@ void gfar_halt(struct net_device *dev)
                         (IEVENT_GRSC | IEVENT_GTSC)))
                        cpu_relax();
        }
+}
+
+/* Halt the receive and transmit queues */
+void gfar_halt(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       struct gfar __iomem *regs = priv->regs;
+       u32 tempval;
+
+       gfar_halt_nodisable(dev);
 
        /* Disable Rx and Tx */
        tempval = gfar_read(&regs->maccfg1);
@@ -1107,6 +1243,7 @@ static int gfar_close(struct net_device *dev)
 
        napi_disable(&priv->napi);
 
+       cancel_work_sync(&priv->reset_task);
        stop_gfar(dev);
 
        /* Disconnect from the PHY */
@@ -1221,20 +1358,31 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
-/* gfar_timeout gets called when a packet has not been
+/* gfar_reset_task gets scheduled when a packet has not been
  * transmitted after a set amount of time.
  * For now, assume that clearing out all the structures, and
- * starting over will fix the problem. */
-static void gfar_timeout(struct net_device *dev)
+ * starting over will fix the problem.
+ */
+static void gfar_reset_task(struct work_struct *work)
 {
-       dev->stats.tx_errors++;
+       struct gfar_private *priv = container_of(work, struct gfar_private,
+                       reset_task);
+       struct net_device *dev = priv->dev;
 
        if (dev->flags & IFF_UP) {
                stop_gfar(dev);
                startup_gfar(dev);
        }
 
-       netif_schedule(dev);
+       netif_tx_schedule_all(dev);
+}
+
+static void gfar_timeout(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+
+       dev->stats.tx_errors++;
+       schedule_work(&priv->reset_task);
 }
 
 /* Interrupt Handler for Transmit complete */
@@ -1725,7 +1873,6 @@ static void adjust_link(struct net_device *dev)
                if (!priv->oldlink) {
                        new_state = 1;
                        priv->oldlink = 1;
-                       netif_schedule(dev);
                }
        } else if (priv->oldlink) {
                new_state = 1;
@@ -1910,7 +2057,12 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
        u32 events = gfar_read(&priv->regs->ievent);
 
        /* Clear IEVENT */
-       gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK);
+       gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);
+
+       /* Magic Packet is not an error. */
+       if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+           (events & IEVENT_MAG))
+               events &= ~IEVENT_MAG;
 
        /* Hmm... */
        if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
@@ -1978,6 +2130,8 @@ MODULE_ALIAS("platform:fsl-gianfar");
 static struct platform_driver gfar_driver = {
        .probe = gfar_probe,
        .remove = gfar_remove,
+       .suspend = gfar_suspend,
+       .resume = gfar_resume,
        .driver = {
                .name = "fsl-gianfar",
                .owner = THIS_MODULE,