diff options
| -rw-r--r-- | arch/sparc64/kernel/pci.c | 6 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 59 | ||||
| -rw-r--r-- | drivers/pci/setup-res.c | 2 | ||||
| -rw-r--r-- | include/linux/pci.h | 2 | ||||
| -rw-r--r-- | include/linux/pci_regs.h | 1 | 
5 files changed, 65 insertions, 5 deletions
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index ec8bf4012c0..9c17591c2a7 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,  	return -EBUSY;  } +void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) +{ +	/* Not implemented for sparc64... */ +	BUG(); +} +  int pci_assign_resource(struct pci_dev *pdev, int resource)  {  	struct pcidev_cookie *pcp = pdev->sysdata; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c62d2f04339..93ec158d06c 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)  }  /** + * pci_restore_bars - restore a devices BAR values (e.g. after wake-up) + * @dev: PCI device to have its BARs restored + * + * Restore the BAR values for a given device, so as to make it + * accessible by its driver. + */ +void +pci_restore_bars(struct pci_dev *dev) +{ +	int i, numres; + +	switch (dev->hdr_type) { +	case PCI_HEADER_TYPE_NORMAL: +		numres = 6; +		break; +	case PCI_HEADER_TYPE_BRIDGE: +		numres = 2; +		break; +	case PCI_HEADER_TYPE_CARDBUS: +		numres = 1; +		break; +	default: +		/* Should never get here, but just in case... */ +		return; +	} + +	for (i = 0; i < numres; i ++) +		pci_update_resource(dev, &dev->resource[i], i); +} + +/**   * pci_set_power_state - Set the power state of a PCI device   * @dev: PCI device to be suspended   * @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);  int  pci_set_power_state(struct pci_dev *dev, pci_power_t state)  { -	int pm; +	int pm, need_restore = 0;  	u16 pmcsr, pmc;  	/* bound the state we're entering */ @@ -278,14 +309,17 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)  			return -EIO;  	} +	pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); +  	/* If we're in D3, force entire word to 0.  	 * This doesn't affect PME_Status, disables PME_En, and  	 * sets PowerState to 0.  	 */ -	if (dev->current_state >= PCI_D3hot) +	if (dev->current_state >= PCI_D3hot) { +		if (!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) +			need_restore = 1;  		pmcsr = 0; -	else { -		pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); +	} else {  		pmcsr &= ~PCI_PM_CTRL_STATE_MASK;  		pmcsr |= state;  	} @@ -308,6 +342,22 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)  		platform_pci_set_power_state(dev, state);  	dev->current_state = state; + +	/* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT +	 * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning +	 * from D3hot to D0 _may_ perform an internal reset, thereby +	 * going to "D0 Uninitialized" rather than "D0 Initialized". +	 * For example, at least some versions of the 3c905B and the +	 * 3c556B exhibit this behaviour. +	 * +	 * At least some laptop BIOSen (e.g. the Thinkpad T21) leave +	 * devices in a D3hot state at boot.  Consequently, we need to +	 * restore at least the BARs so that the device will be +	 * accessible to its driver. +	 */ +	if (need_restore) +		pci_restore_bars(dev); +  	return 0;  } @@ -809,6 +859,7 @@ struct pci_dev *isa_bridge;  EXPORT_SYMBOL(isa_bridge);  #endif +EXPORT_SYMBOL_GPL(pci_restore_bars);  EXPORT_SYMBOL(pci_enable_device_bars);  EXPORT_SYMBOL(pci_enable_device);  EXPORT_SYMBOL(pci_disable_device); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 5598b4714f7..362f93337a3 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -26,7 +26,7 @@  #include "pci.h" -static void +void  pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)  {  	struct pci_bus_region region; diff --git a/include/linux/pci.h b/include/linux/pci.h index 830c1bae56c..8878ccff9e3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -384,7 +384,9 @@ int pci_set_mwi(struct pci_dev *dev);  void pci_clear_mwi(struct pci_dev *dev);  int pci_set_dma_mask(struct pci_dev *dev, u64 mask);  int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask); +void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);  int pci_assign_resource(struct pci_dev *dev, int i); +void pci_restore_bars(struct pci_dev *dev);  /* ROM control related routines */  void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 7dc391cad10..e2a089b051e 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -222,6 +222,7 @@  #define  PCI_PM_CAP_PME_D3cold	0x8000	/* PME# from D3 (cold) */  #define PCI_PM_CTRL		4	/* PM control and status register */  #define  PCI_PM_CTRL_STATE_MASK	0x0003	/* Current power state (D0 to D3) */ +#define  PCI_PM_CTRL_NO_SOFT_RESET	0x0004	/* No reset for D3hot->D0 */  #define  PCI_PM_CTRL_PME_ENABLE	0x0100	/* PME pin enable */  #define  PCI_PM_CTRL_DATA_SEL_MASK	0x1e00	/* Data select (??) */  #define  PCI_PM_CTRL_DATA_SCALE_MASK	0x6000	/* Data scale (??) */  |