diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-12-31 06:15:54 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-01-04 18:41:47 -0500 |
commit | 1ae861e652b5457e7fa98ccbc55abea1e207916e (patch) | |
tree | d0326aab2746a779f3ab140ec9fdea2508f2e99b | |
parent | 6be954d1f91b81ca85c74792b13654069278c577 (diff) |
PCI/PM: Use per-device D3 delays
It turns out that some PCI devices require extra delays when changing
power state from D3 to D0 (and the other way around). Although this
is against the PCI specification, we can handle it quite easily by
allowing drivers to define arbitrary D3 delays for devices known to
require extra time for switching power states.
Introduce additional field d3_delay in struct pci_dev and use it to
store the value of the device's D0->D3 delay, in miliseconds. Make
the PCI PM core code use the per-device d3_delay unless
pci_pm_d3_delay is greater (in which case the latter is used).
[This also allows the driver to specify d3_delay shorter than the
10 ms required by the PCI standard if the device is known to be able
to handle that.]
Make the sky2 driver set d3_delay to 150 for devices handled by it.
Fixes http://bugzilla.kernel.org/show_bug.cgi?id=14730 which is a
listed regression from 2.6.30.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | drivers/net/sky2.c | 1 | ||||
-rw-r--r-- | drivers/pci/pci.c | 19 | ||||
-rw-r--r-- | include/linux/pci.h | 1 |
3 files changed, 17 insertions, 4 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1c01b96c9611..2d28d58200d0 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -4684,6 +4684,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
4684 | INIT_WORK(&hw->restart_work, sky2_restart); | 4684 | INIT_WORK(&hw->restart_work, sky2_restart); |
4685 | 4685 | ||
4686 | pci_set_drvdata(pdev, hw); | 4686 | pci_set_drvdata(pdev, hw); |
4687 | pdev->d3_delay = 150; | ||
4687 | 4688 | ||
4688 | return 0; | 4689 | return 0; |
4689 | 4690 | ||
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0906599ebfde..315fea47e784 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -29,7 +29,17 @@ const char *pci_power_names[] = { | |||
29 | }; | 29 | }; |
30 | EXPORT_SYMBOL_GPL(pci_power_names); | 30 | EXPORT_SYMBOL_GPL(pci_power_names); |
31 | 31 | ||
32 | unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; | 32 | unsigned int pci_pm_d3_delay; |
33 | |||
34 | static void pci_dev_d3_sleep(struct pci_dev *dev) | ||
35 | { | ||
36 | unsigned int delay = dev->d3_delay; | ||
37 | |||
38 | if (delay < pci_pm_d3_delay) | ||
39 | delay = pci_pm_d3_delay; | ||
40 | |||
41 | msleep(delay); | ||
42 | } | ||
33 | 43 | ||
34 | #ifdef CONFIG_PCI_DOMAINS | 44 | #ifdef CONFIG_PCI_DOMAINS |
35 | int pci_domains_supported = 1; | 45 | int pci_domains_supported = 1; |
@@ -522,7 +532,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) | |||
522 | /* Mandatory power management transition delays */ | 532 | /* Mandatory power management transition delays */ |
523 | /* see PCI PM 1.1 5.6.1 table 18 */ | 533 | /* see PCI PM 1.1 5.6.1 table 18 */ |
524 | if (state == PCI_D3hot || dev->current_state == PCI_D3hot) | 534 | if (state == PCI_D3hot || dev->current_state == PCI_D3hot) |
525 | msleep(pci_pm_d3_delay); | 535 | pci_dev_d3_sleep(dev); |
526 | else if (state == PCI_D2 || dev->current_state == PCI_D2) | 536 | else if (state == PCI_D2 || dev->current_state == PCI_D2) |
527 | udelay(PCI_PM_D2_DELAY); | 537 | udelay(PCI_PM_D2_DELAY); |
528 | 538 | ||
@@ -1409,6 +1419,7 @@ void pci_pm_init(struct pci_dev *dev) | |||
1409 | } | 1419 | } |
1410 | 1420 | ||
1411 | dev->pm_cap = pm; | 1421 | dev->pm_cap = pm; |
1422 | dev->d3_delay = PCI_PM_D3_WAIT; | ||
1412 | 1423 | ||
1413 | dev->d1_support = false; | 1424 | dev->d1_support = false; |
1414 | dev->d2_support = false; | 1425 | dev->d2_support = false; |
@@ -2247,12 +2258,12 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) | |||
2247 | csr &= ~PCI_PM_CTRL_STATE_MASK; | 2258 | csr &= ~PCI_PM_CTRL_STATE_MASK; |
2248 | csr |= PCI_D3hot; | 2259 | csr |= PCI_D3hot; |
2249 | pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); | 2260 | pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); |
2250 | msleep(pci_pm_d3_delay); | 2261 | pci_dev_d3_sleep(dev); |
2251 | 2262 | ||
2252 | csr &= ~PCI_PM_CTRL_STATE_MASK; | 2263 | csr &= ~PCI_PM_CTRL_STATE_MASK; |
2253 | csr |= PCI_D0; | 2264 | csr |= PCI_D0; |
2254 | pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); | 2265 | pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); |
2255 | msleep(pci_pm_d3_delay); | 2266 | pci_dev_d3_sleep(dev); |
2256 | 2267 | ||
2257 | return 0; | 2268 | return 0; |
2258 | } | 2269 | } |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 5da0690d9cee..174e5392e51e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -243,6 +243,7 @@ struct pci_dev { | |||
243 | unsigned int d2_support:1; /* Low power state D2 is supported */ | 243 | unsigned int d2_support:1; /* Low power state D2 is supported */ |
244 | unsigned int no_d1d2:1; /* Only allow D0 and D3 */ | 244 | unsigned int no_d1d2:1; /* Only allow D0 and D3 */ |
245 | unsigned int wakeup_prepared:1; | 245 | unsigned int wakeup_prepared:1; |
246 | unsigned int d3_delay; /* D3->D0 transition time in ms */ | ||
246 | 247 | ||
247 | #ifdef CONFIG_PCIEASPM | 248 | #ifdef CONFIG_PCIEASPM |
248 | struct pcie_link_state *link_state; /* ASPM link state. */ | 249 | struct pcie_link_state *link_state; /* ASPM link state. */ |