From d374c1c1281d6188a0d0676172b1c0e3de35c6e7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 12 Jun 2006 12:53:27 -0700 Subject: [sky2] Fix sky2 network driver suspend/resume This fixes two independent problems: it would not save the PCI state on suspend (and thus try to resume a nonexistent state on resume), and while shut off, if an interrupt happened on the same shared irq, the irq handler would react very badly to the interrupt status being an invalid all-ones state. Acked-by: Jeff Garzik Signed-off-by: Linus Torvalds --- drivers/net/sky2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 959109609d85..6b87c7a5c906 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2183,6 +2183,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); + if (!~status) + return 0; + if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -3438,6 +3441,7 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) } } + pci_save_state(pdev); return sky2_set_power_state(hw, pci_choose_state(pdev, state)); } -- cgit v1.2.2 From 2ccc99b7b71976d15822ae7c41cd2ccda66d5076 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 13 Jun 2006 17:17:27 +0900 Subject: [PATCH] sky2: set_power_state should be void The set power state function is cleaner if it doesn't return anything. The only caller that could fail is in suspend() and it can check the argument there. Signed-off-by: Stephen Hemminger Signed-off-by: Linus Torvalds --- drivers/net/sky2.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6b87c7a5c906..765c8f063512 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -187,12 +187,11 @@ static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) return v; } -static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) +static void sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) { u16 power_control; u32 reg1; int vaux; - int ret = 0; pr_debug("sky2_set_power_state %d\n", state); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); @@ -275,12 +274,10 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) break; default: printk(KERN_ERR PFX "Unknown power state %d\n", state); - ret = -1; } sky2_pci_write16(hw, hw->pm_cap + PCI_PM_CTRL, power_control); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); - return ret; } static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) @@ -3428,6 +3425,10 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) { struct sky2_hw *hw = pci_get_drvdata(pdev); int i; + pci_power_t pstate = pci_choose_state(pdev, state); + + if (!(pstate == PCI_D3hot || pstate == PCI_D3cold)) + return -EINVAL; for (i = 0; i < 2; i++) { struct net_device *dev = hw->dev[i]; @@ -3442,7 +3443,8 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) } pci_save_state(pdev); - return sky2_set_power_state(hw, pci_choose_state(pdev, state)); + sky2_set_power_state(hw, pstate); + return 0; } static int sky2_resume(struct pci_dev *pdev) @@ -3452,9 +3454,7 @@ static int sky2_resume(struct pci_dev *pdev) pci_restore_state(pdev); pci_enable_wake(pdev, PCI_D0, 0); - err = sky2_set_power_state(hw, PCI_D0); - if (err) - goto out; + sky2_set_power_state(hw, PCI_D0); err = sky2_reset(hw); if (err) -- cgit v1.2.2 From f05267e7dee58741a4feb20d0351706ec64bb0b5 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 13 Jun 2006 17:17:28 +0900 Subject: [PATCH] sky2: don't hard code number of ports It is cleaner, to not loop over both ports if only one exists. Signed-off-by: Stephen Hemminger Signed-off-by: Linus Torvalds --- drivers/net/sky2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 765c8f063512..6ad676d2cbc1 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -3430,7 +3430,7 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) if (!(pstate == PCI_D3hot || pstate == PCI_D3cold)) return -EINVAL; - for (i = 0; i < 2; i++) { + for (i = 0; i < hw->ports; i++) { struct net_device *dev = hw->dev[i]; if (dev) { @@ -3460,7 +3460,7 @@ static int sky2_resume(struct pci_dev *pdev) if (err) goto out; - for (i = 0; i < 2; i++) { + for (i = 0; i < hw->ports; i++) { struct net_device *dev = hw->dev[i]; if (dev && netif_running(dev)) { netif_device_attach(dev); -- cgit v1.2.2 From 26ec43f132d1cf282124a020b2bb5310496c9132 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 13 Jun 2006 17:17:29 +0900 Subject: [PATCH] sky2: fix hotplug detect during poll If the poll routine detects no hardware available, it needs to dequeue it self from the network poll list. Linus didn't understand NAPI. Signed-off-by: Stephen Hemminger Signed-off-by: Linus Torvalds --- drivers/net/sky2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6ad676d2cbc1..b680e64ad2ca 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2181,7 +2181,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) u32 status = sky2_read32(hw, B0_Y2_SP_EISR); if (!~status) - return 0; + goto out; if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2219,7 +2219,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) if (sky2_more_work(hw)) return 1; - +out: netif_rx_complete(dev0); sky2_read32(hw, B0_Y2_SP_LISR); -- cgit v1.2.2 From 8ab8fca2071cec559e4b77212cccffd150ce5ce7 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 13 Jun 2006 17:17:30 +0900 Subject: [PATCH] sky2: save/restore base hardware irq during suspend/resume The hardware should be fully shut off during suspend, and the base irq mask restored during resume. Signed-off-by: Stephen Hemminger Signed-off-by: Linus Torvalds --- drivers/net/sky2.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index b680e64ad2ca..df899dcbd5b0 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -3442,6 +3442,7 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) } } + sky2_write32(hw, B0_IMSK, 0); pci_save_state(pdev); sky2_set_power_state(hw, pstate); return 0; @@ -3460,6 +3461,8 @@ static int sky2_resume(struct pci_dev *pdev) if (err) goto out; + sky2_write32(hw, B0_IMSK, Y2_IS_BASE); + for (i = 0; i < hw->ports; i++) { struct net_device *dev = hw->dev[i]; if (dev && netif_running(dev)) { -- cgit v1.2.2 From eb35cf60e462491249166182e3e755d3d5d91a28 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 13 Jun 2006 17:17:31 +0900 Subject: [PATCH] sky2: stop/start hardware idle timer on suspend/resume The resume bug was caused not by an early interrupt but because the idle timeout was not being stopped on suspend. Also disable hardware IRQ's on suspend. Will need to revisit this with hotplug? Signed-off-by: Stephen Hemminger Signed-off-by: Linus Torvalds --- drivers/net/sky2.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index df899dcbd5b0..97fe95666f3b 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2161,6 +2161,13 @@ static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port, /* If idle then force a fake soft NAPI poll once a second * to work around cases where sharing an edge triggered interrupt. */ +static inline void sky2_idle_start(struct sky2_hw *hw) +{ + if (idle_timeout > 0) + mod_timer(&hw->idle_timer, + jiffies + msecs_to_jiffies(idle_timeout)); +} + static void sky2_idle(unsigned long arg) { struct sky2_hw *hw = (struct sky2_hw *) arg; @@ -3350,9 +3357,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, sky2_write32(hw, B0_IMSK, Y2_IS_BASE); setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw); - if (idle_timeout > 0) - mod_timer(&hw->idle_timer, - jiffies + msecs_to_jiffies(idle_timeout)); + sky2_idle_start(hw); pci_set_drvdata(pdev, hw); @@ -3430,6 +3435,8 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) if (!(pstate == PCI_D3hot || pstate == PCI_D3cold)) return -EINVAL; + del_timer_sync(&hw->idle_timer); + for (i = 0; i < hw->ports; i++) { struct net_device *dev = hw->dev[i]; @@ -3472,10 +3479,12 @@ static int sky2_resume(struct pci_dev *pdev) printk(KERN_ERR PFX "%s: could not up: %d\n", dev->name, err); dev_close(dev); - break; + goto out; } } } + + sky2_idle_start(hw); out: return err; } -- cgit v1.2.2