Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[pandora-kernel.git] / drivers / net / forcedeth.c
index 20d4fe9..4ed89fa 100644 (file)
@@ -426,6 +426,7 @@ union ring_type {
 #define NV_PCI_REGSZ_VER1              0x270
 #define NV_PCI_REGSZ_VER2              0x2d4
 #define NV_PCI_REGSZ_VER3              0x604
+#define NV_PCI_REGSZ_MAX               0x604
 
 /* various timeout delays: all in usec */
 #define NV_TXRX_RESET_DELAY    4
@@ -784,6 +785,9 @@ struct fe_priv {
 
        /* flow control */
        u32 pause_flags;
+
+       /* power saved state */
+       u32 saved_config_space[NV_PCI_REGSZ_MAX/4];
 };
 
 /*
@@ -2827,6 +2831,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu)
                 */
                nv_disable_irq(dev);
                netif_tx_lock_bh(dev);
+               netif_addr_lock(dev);
                spin_lock(&np->lock);
                /* stop engines */
                nv_stop_rxtx(dev);
@@ -2851,6 +2856,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu)
                /* restart rx engine */
                nv_start_rxtx(dev);
                spin_unlock(&np->lock);
+               netif_addr_unlock(dev);
                netif_tx_unlock_bh(dev);
                nv_enable_irq(dev);
        }
@@ -2887,6 +2893,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr)
 
        if (netif_running(dev)) {
                netif_tx_lock_bh(dev);
+               netif_addr_lock(dev);
                spin_lock_irq(&np->lock);
 
                /* stop rx engine */
@@ -2898,6 +2905,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr)
                /* restart rx engine */
                nv_start_rx(dev);
                spin_unlock_irq(&np->lock);
+               netif_addr_unlock(dev);
                netif_tx_unlock_bh(dev);
        } else {
                nv_copy_mac_to_hw(dev);
@@ -3967,6 +3975,7 @@ static void nv_do_nic_poll(unsigned long data)
                printk(KERN_INFO "forcedeth: MAC in recoverable error state\n");
                if (netif_running(dev)) {
                        netif_tx_lock_bh(dev);
+                       netif_addr_lock(dev);
                        spin_lock(&np->lock);
                        /* stop engines */
                        nv_stop_rxtx(dev);
@@ -3991,6 +4000,7 @@ static void nv_do_nic_poll(unsigned long data)
                        /* restart rx engine */
                        nv_start_rxtx(dev);
                        spin_unlock(&np->lock);
+                       netif_addr_unlock(dev);
                        netif_tx_unlock_bh(dev);
                }
        }
@@ -4198,6 +4208,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 
                nv_disable_irq(dev);
                netif_tx_lock_bh(dev);
+               netif_addr_lock(dev);
                /* with plain spinlock lockdep complains */
                spin_lock_irqsave(&np->lock, flags);
                /* stop engines */
@@ -4211,6 +4222,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                 */
                nv_stop_rxtx(dev);
                spin_unlock_irqrestore(&np->lock, flags);
+               netif_addr_unlock(dev);
                netif_tx_unlock_bh(dev);
        }
 
@@ -4356,10 +4368,12 @@ static int nv_nway_reset(struct net_device *dev)
                if (netif_running(dev)) {
                        nv_disable_irq(dev);
                        netif_tx_lock_bh(dev);
+                       netif_addr_lock(dev);
                        spin_lock(&np->lock);
                        /* stop engines */
                        nv_stop_rxtx(dev);
                        spin_unlock(&np->lock);
+                       netif_addr_unlock(dev);
                        netif_tx_unlock_bh(dev);
                        printk(KERN_INFO "%s: link down.\n", dev->name);
                }
@@ -4467,6 +4481,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
        if (netif_running(dev)) {
                nv_disable_irq(dev);
                netif_tx_lock_bh(dev);
+               netif_addr_lock(dev);
                spin_lock(&np->lock);
                /* stop engines */
                nv_stop_rxtx(dev);
@@ -4515,6 +4530,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
                /* restart engines */
                nv_start_rxtx(dev);
                spin_unlock(&np->lock);
+               netif_addr_unlock(dev);
                netif_tx_unlock_bh(dev);
                nv_enable_irq(dev);
        }
@@ -4552,10 +4568,12 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam*
        if (netif_running(dev)) {
                nv_disable_irq(dev);
                netif_tx_lock_bh(dev);
+               netif_addr_lock(dev);
                spin_lock(&np->lock);
                /* stop engines */
                nv_stop_rxtx(dev);
                spin_unlock(&np->lock);
+               netif_addr_unlock(dev);
                netif_tx_unlock_bh(dev);
        }
 
@@ -4942,6 +4960,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
                        napi_disable(&np->napi);
 #endif
                        netif_tx_lock_bh(dev);
+                       netif_addr_lock(dev);
                        spin_lock_irq(&np->lock);
                        nv_disable_hw_interrupts(dev, np->irqmask);
                        if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
@@ -4955,6 +4974,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
                        /* drain rx queue */
                        nv_drain_rxtx(dev);
                        spin_unlock_irq(&np->lock);
+                       netif_addr_unlock(dev);
                        netif_tx_unlock_bh(dev);
                }
 
@@ -5566,6 +5586,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
        /* set mac address */
        nv_copy_mac_to_hw(dev);
 
+       /* Workaround current PCI init glitch:  wakeup bits aren't
+        * being set from PCI PM capability.
+        */
+       device_init_wakeup(&pci_dev->dev, 1);
+
        /* disable WOL */
        writel(0, base + NvRegWakeUpFlags);
        np->wolenabled = 0;
@@ -5816,50 +5841,66 @@ static int nv_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct fe_priv *np = netdev_priv(dev);
+       u8 __iomem *base = get_hwbase(dev);
+       int i;
 
-       if (!netif_running(dev))
-               goto out;
-
+       if (netif_running(dev)) {
+               // Gross.
+               nv_close(dev);
+       }
        netif_device_detach(dev);
 
-       // Gross.
-       nv_close(dev);
+       /* save non-pci configuration space */
+       for (i = 0;i <= np->register_size/sizeof(u32); i++)
+               np->saved_config_space[i] = readl(base + i*sizeof(u32));
 
        pci_save_state(pdev);
        pci_enable_wake(pdev, pci_choose_state(pdev, state), np->wolenabled);
+       pci_disable_device(pdev);
        pci_set_power_state(pdev, pci_choose_state(pdev, state));
-out:
        return 0;
 }
 
 static int nv_resume(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
+       struct fe_priv *np = netdev_priv(dev);
        u8 __iomem *base = get_hwbase(dev);
-       int rc = 0;
-       u32 txreg;
-
-       if (!netif_running(dev))
-               goto out;
-
-       netif_device_attach(dev);
+       int i, rc = 0;
 
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
+       /* ack any pending wake events, disable PME */
        pci_enable_wake(pdev, PCI_D0, 0);
 
-       /* restore mac address reverse flag */
-       txreg = readl(base + NvRegTransmitPoll);
-       txreg |= NVREG_TRANSMITPOLL_MAC_ADDR_REV;
-       writel(txreg, base + NvRegTransmitPoll);
+       /* restore non-pci configuration space */
+       for (i = 0;i <= np->register_size/sizeof(u32); i++)
+               writel(np->saved_config_space[i], base+i*sizeof(u32));
 
-       rc = nv_open(dev);
-       nv_set_multicast(dev);
-out:
+       netif_device_attach(dev);
+       if (netif_running(dev)) {
+               rc = nv_open(dev);
+               nv_set_multicast(dev);
+       }
        return rc;
 }
+
+static void nv_shutdown(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct fe_priv *np = netdev_priv(dev);
+
+       if (netif_running(dev))
+               nv_close(dev);
+
+       pci_enable_wake(pdev, PCI_D3hot, np->wolenabled);
+       pci_enable_wake(pdev, PCI_D3cold, np->wolenabled);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+}
 #else
 #define nv_suspend NULL
+#define nv_shutdown NULL
 #define nv_resume NULL
 #endif /* CONFIG_PM */
 
@@ -6030,6 +6071,7 @@ static struct pci_driver driver = {
        .remove         = __devexit_p(nv_remove),
        .suspend        = nv_suspend,
        .resume         = nv_resume,
+       .shutdown       = nv_shutdown,
 };
 
 static int __init init_nic(void)