aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2005-08-04 21:06:10 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-08-05 00:32:46 -0400
commitfec59a711eef002d4ef9eb8de09dd0a26986eb77 (patch)
tree4287cefdd94ce686ca0cad40f7897d8acd9c89dc
parentc306895167c8384b88bc02945a0d226a04218fa5 (diff)
[PATCH] PCI: restore BAR values after D3hot->D0 for devices that need it
Some PCI devices (e.g. 3c905B, 3c556B) lose all configuration (including BARs) when transitioning from D3hot->D0. This leaves such a device in an inaccessible state. The patch below causes the BARs to be restored when enabling such a device, so that its driver will be able to access it. The patch also adds pci_restore_bars as a new global symbol, and adds a correpsonding EXPORT_SYMBOL_GPL for that. Some firmware (e.g. Thinkpad T21) leaves devices in D3hot after a (re)boot. Most drivers call pci_enable_device very early, so devices left in D3hot that lose configuration during the D3hot->D0 transition will be inaccessible to their drivers. Drivers could be modified to account for this, but it would be difficult to know which drivers need modification. This is especially true since often many devices are covered by the same driver. It likely would be necessary to replicate code across dozens of drivers. The patch below should trigger only when transitioning from D3hot->D0 (or at boot), and only for devices that have the "no soft reset" bit cleared in the PM control register. I believe it is safe to include this patch as part of the PCI infrastructure. The cleanest implementation of pci_restore_bars was to call pci_update_resource. Unfortunately, that does not currently exist for the sparc64 architecture. The patch below includes a null implemenation of pci_update_resource for sparc64. Some have expressed interest in making general use of the the pci_restore_bars function, so that has been exported to GPL licensed modules. Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/sparc64/kernel/pci.c6
-rw-r--r--drivers/pci/pci.c59
-rw-r--r--drivers/pci/setup-res.c2
-rw-r--r--include/linux/pci.h3
4 files changed, 65 insertions, 5 deletions
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index bba140d98b1b..914e125d3971 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -413,6 +413,12 @@ static int pci_assign_bus_resource(const struct pci_bus *bus,
413 return -EBUSY; 413 return -EBUSY;
414} 414}
415 415
416void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
417{
418 /* Not implemented for sparc64... */
419 BUG();
420}
421
416int pci_assign_resource(struct pci_dev *pdev, int resource) 422int pci_assign_resource(struct pci_dev *pdev, int resource)
417{ 423{
418 struct pcidev_cookie *pcp = pdev->sysdata; 424 struct pcidev_cookie *pcp = pdev->sysdata;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 1b34fc56067e..65ea7d25f691 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -222,6 +222,37 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
222} 222}
223 223
224/** 224/**
225 * pci_restore_bars - restore a devices BAR values (e.g. after wake-up)
226 * @dev: PCI device to have its BARs restored
227 *
228 * Restore the BAR values for a given device, so as to make it
229 * accessible by its driver.
230 */
231void
232pci_restore_bars(struct pci_dev *dev)
233{
234 int i, numres;
235
236 switch (dev->hdr_type) {
237 case PCI_HEADER_TYPE_NORMAL:
238 numres = 6;
239 break;
240 case PCI_HEADER_TYPE_BRIDGE:
241 numres = 2;
242 break;
243 case PCI_HEADER_TYPE_CARDBUS:
244 numres = 1;
245 break;
246 default:
247 /* Should never get here, but just in case... */
248 return;
249 }
250
251 for (i = 0; i < numres; i ++)
252 pci_update_resource(dev, &dev->resource[i], i);
253}
254
255/**
225 * pci_set_power_state - Set the power state of a PCI device 256 * pci_set_power_state - Set the power state of a PCI device
226 * @dev: PCI device to be suspended 257 * @dev: PCI device to be suspended
227 * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering 258 * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
@@ -239,7 +270,7 @@ int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
239int 270int
240pci_set_power_state(struct pci_dev *dev, pci_power_t state) 271pci_set_power_state(struct pci_dev *dev, pci_power_t state)
241{ 272{
242 int pm; 273 int pm, need_restore = 0;
243 u16 pmcsr, pmc; 274 u16 pmcsr, pmc;
244 275
245 /* bound the state we're entering */ 276 /* bound the state we're entering */
@@ -278,14 +309,17 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
278 return -EIO; 309 return -EIO;
279 } 310 }
280 311
312 pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
313
281 /* If we're in D3, force entire word to 0. 314 /* If we're in D3, force entire word to 0.
282 * This doesn't affect PME_Status, disables PME_En, and 315 * This doesn't affect PME_Status, disables PME_En, and
283 * sets PowerState to 0. 316 * sets PowerState to 0.
284 */ 317 */
285 if (dev->current_state >= PCI_D3hot) 318 if (dev->current_state >= PCI_D3hot) {
319 if (!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
320 need_restore = 1;
286 pmcsr = 0; 321 pmcsr = 0;
287 else { 322 } else {
288 pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
289 pmcsr &= ~PCI_PM_CTRL_STATE_MASK; 323 pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
290 pmcsr |= state; 324 pmcsr |= state;
291 } 325 }
@@ -308,6 +342,22 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
308 platform_pci_set_power_state(dev, state); 342 platform_pci_set_power_state(dev, state);
309 343
310 dev->current_state = state; 344 dev->current_state = state;
345
346 /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
347 * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
348 * from D3hot to D0 _may_ perform an internal reset, thereby
349 * going to "D0 Uninitialized" rather than "D0 Initialized".
350 * For example, at least some versions of the 3c905B and the
351 * 3c556B exhibit this behaviour.
352 *
353 * At least some laptop BIOSen (e.g. the Thinkpad T21) leave
354 * devices in a D3hot state at boot. Consequently, we need to
355 * restore at least the BARs so that the device will be
356 * accessible to its driver.
357 */
358 if (need_restore)
359 pci_restore_bars(dev);
360
311 return 0; 361 return 0;
312} 362}
313 363
@@ -805,6 +855,7 @@ struct pci_dev *isa_bridge;
805EXPORT_SYMBOL(isa_bridge); 855EXPORT_SYMBOL(isa_bridge);
806#endif 856#endif
807 857
858EXPORT_SYMBOL_GPL(pci_restore_bars);
808EXPORT_SYMBOL(pci_enable_device_bars); 859EXPORT_SYMBOL(pci_enable_device_bars);
809EXPORT_SYMBOL(pci_enable_device); 860EXPORT_SYMBOL(pci_enable_device);
810EXPORT_SYMBOL(pci_disable_device); 861EXPORT_SYMBOL(pci_disable_device);
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 1ca21d2ba11c..878fd0a65c02 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -26,7 +26,7 @@
26#include "pci.h" 26#include "pci.h"
27 27
28 28
29static void 29void
30pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) 30pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
31{ 31{
32 struct pci_bus_region region; 32 struct pci_bus_region region;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 8621cf42b46f..98bdd95fcee9 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -225,6 +225,7 @@
225#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ 225#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
226#define PCI_PM_CTRL 4 /* PM control and status register */ 226#define PCI_PM_CTRL 4 /* PM control and status register */
227#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ 227#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
228#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */
228#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ 229#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */
229#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ 230#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */
230#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ 231#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */
@@ -816,7 +817,9 @@ int pci_set_mwi(struct pci_dev *dev);
816void pci_clear_mwi(struct pci_dev *dev); 817void pci_clear_mwi(struct pci_dev *dev);
817int pci_set_dma_mask(struct pci_dev *dev, u64 mask); 818int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
818int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask); 819int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
820void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);
819int pci_assign_resource(struct pci_dev *dev, int i); 821int pci_assign_resource(struct pci_dev *dev, int i);
822void pci_restore_bars(struct pci_dev *dev);
820 823
821/* ROM control related routines */ 824/* ROM control related routines */
822void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size); 825void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size);