summaryrefslogtreecommitdiffstats
path: root/drivers/net/forcedeth.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/forcedeth.c')
-rw-r--r--drivers/net/forcedeth.c97
1 files changed, 75 insertions, 22 deletions
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 2cb244763292..4ed89fa9ae46 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -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);
}
}
@@ -4194,12 +4204,25 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
netif_carrier_off(dev);
if (netif_running(dev)) {
+ unsigned long flags;
+
nv_disable_irq(dev);
netif_tx_lock_bh(dev);
- spin_lock(&np->lock);
+ netif_addr_lock(dev);
+ /* with plain spinlock lockdep complains */
+ spin_lock_irqsave(&np->lock, flags);
/* stop engines */
+ /* FIXME:
+ * this can take some time, and interrupts are disabled
+ * due to spin_lock_irqsave, but let's hope no daemon
+ * is going to change the settings very often...
+ * Worst case:
+ * NV_RXSTOP_DELAY1MAX + NV_TXSTOP_DELAY1MAX
+ * + some minor delays, which is up to a second approximately
+ */
nv_stop_rxtx(dev);
- spin_unlock(&np->lock);
+ spin_unlock_irqrestore(&np->lock, flags);
+ netif_addr_unlock(dev);
netif_tx_unlock_bh(dev);
}
@@ -4345,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);
}
@@ -4456,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);
@@ -4504,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);
}
@@ -4541,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);
}
@@ -4931,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)) {
@@ -4944,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);
}
@@ -5555,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;
@@ -5805,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 */
@@ -6019,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)