Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / net / forcedeth.c
index 2cb2447..c980ce9 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];
 };
 
 /*
@@ -5805,50 +5809,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 */
 
@@ -6019,6 +6039,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)