@ -587,7 +587,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
dev_info ( & dev - > dev , " Refused to change power state, "
" currently in D%d \n " , dev - > current_state ) ;
/* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
/*
* 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 " .
@ -619,6 +620,16 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
if ( dev - > pm_cap ) {
u16 pmcsr ;
/*
* Configuration space is not accessible for device in
* D3cold , so just keep or set D3cold for safety
*/
if ( dev - > current_state = = PCI_D3cold )
return ;
if ( state = = PCI_D3cold ) {
dev - > current_state = PCI_D3cold ;
return ;
}
pci_read_config_word ( dev , dev - > pm_cap + PCI_PM_CTRL , & pmcsr ) ;
dev - > current_state = ( pmcsr & PCI_PM_CTRL_STATE_MASK ) ;
} else {
@ -659,8 +670,50 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state)
*/
static void __pci_start_power_transition ( struct pci_dev * dev , pci_power_t state )
{
if ( state = = PCI_D0 )
if ( state = = PCI_D0 ) {
pci_platform_power_transition ( dev , PCI_D0 ) ;
/*
* Mandatory power management transition delays , see
* PCI Express Base Specification Revision 2.0 Section
* 6.6 .1 : Conventional Reset . Do not delay for
* devices powered on / off by corresponding bridge ,
* because have already delayed for the bridge .
*/
if ( dev - > runtime_d3cold ) {
msleep ( dev - > d3cold_delay ) ;
/*
* When powering on a bridge from D3cold , the
* whole hierarchy may be powered on into
* D0uninitialized state , resume them to give
* them a chance to suspend again
*/
pci_wakeup_bus ( dev - > subordinate ) ;
}
}
}
/**
* __pci_dev_set_current_state - Set current state of a PCI device
* @ dev : Device to handle
* @ data : pointer to state to be set
*/
static int __pci_dev_set_current_state ( struct pci_dev * dev , void * data )
{
pci_power_t state = * ( pci_power_t * ) data ;
dev - > current_state = state ;
return 0 ;
}
/**
* __pci_bus_set_current_state - Walk given bus and set current state of devices
* @ bus : Top bus of the subtree to walk .
* @ state : state to be set
*/
static void __pci_bus_set_current_state ( struct pci_bus * bus , pci_power_t state )
{
if ( bus )
pci_walk_bus ( bus , __pci_dev_set_current_state , & state ) ;
}
/**
@ -672,8 +725,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
*/
int __pci_complete_power_transition ( struct pci_dev * dev , pci_power_t state )
{
return state > = PCI_D0 ?
pci_platform_power_transition ( dev , state ) : - EINVAL ;
int ret ;
if ( state < PCI_D0 )
return - EINVAL ;
ret = pci_platform_power_transition ( dev , state ) ;
/* Power off the bridge may power off the whole hierarchy */
if ( ! ret & & state = = PCI_D3cold )
__pci_bus_set_current_state ( dev - > subordinate , PCI_D3cold ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( __pci_complete_power_transition ) ;
@ -697,8 +757,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
int error ;
/* bound the state we're entering */
if ( state > PCI_D3hot )
state = PCI_D3hot ;
if ( state > PCI_D3cold )
state = PCI_D3cold ;
else if ( state < PCI_D0 )
state = PCI_D0 ;
else if ( ( state = = PCI_D1 | | state = = PCI_D2 ) & & pci_no_d1d2 ( dev ) )
@ -713,10 +773,15 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
/* This device is quirked not to be put into D3, so
don ' t put it in D3 */
if ( state = = PCI_D3hot & & ( dev - > dev_flags & PCI_DEV_FLAGS_NO_D3 ) )
if ( state > = PCI_D3hot & & ( dev - > dev_flags & PCI_DEV_FLAGS_NO_D3 ) )
return 0 ;
error = pci_raw_set_power_state ( dev , state ) ;
/*
* To put device in D3cold , we put device into D3hot in native
* way , then put device into D3cold with platform ops
*/
error = pci_raw_set_power_state ( dev , state > PCI_D3hot ?
PCI_D3hot : state ) ;
if ( ! __pci_complete_power_transition ( dev , state ) )
error = 0 ;
@ -1459,6 +1524,28 @@ void pci_pme_wakeup_bus(struct pci_bus *bus)
pci_walk_bus ( bus , pci_pme_wakeup , ( void * ) true ) ;
}
/**
* pci_wakeup - Wake up a PCI device
* @ dev : Device to handle .
* @ ign : ignored parameter
*/
static int pci_wakeup ( struct pci_dev * pci_dev , void * ign )
{
pci_wakeup_event ( pci_dev ) ;
pm_request_resume ( & pci_dev - > dev ) ;
return 0 ;
}
/**
* pci_wakeup_bus - Walk given bus and wake up devices on it
* @ bus : Top bus of the subtree to walk .
*/
void pci_wakeup_bus ( struct pci_bus * bus )
{
if ( bus )
pci_walk_bus ( bus , pci_wakeup , NULL ) ;
}
/**
* pci_pme_capable - check the capability of PCI device to generate PME #
* @ dev : PCI device to handle .
@ -1480,6 +1567,16 @@ static void pci_pme_list_scan(struct work_struct *work)
if ( ! list_empty ( & pci_pme_list ) ) {
list_for_each_entry_safe ( pme_dev , n , & pci_pme_list , list ) {
if ( pme_dev - > dev - > pme_poll ) {
struct pci_dev * bridge ;
bridge = pme_dev - > dev - > bus - > self ;
/*
* If bridge is in low power state , the
* configuration space of subordinate devices
* may be not accessible
*/
if ( bridge & & bridge - > current_state ! = PCI_D0 )
continue ;
pci_pme_wakeup ( pme_dev - > dev , NULL ) ;
} else {
list_del ( & pme_dev - > list ) ;
@ -1706,6 +1803,10 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
if ( target_state = = PCI_POWER_ERROR )
return - EIO ;
/* D3cold during system suspend/hibernate is not supported */
if ( target_state > PCI_D3hot )
target_state = PCI_D3hot ;
pci_enable_wake ( dev , target_state , device_may_wakeup ( & dev - > dev ) ) ;
error = pci_set_power_state ( dev , target_state ) ;
@ -1743,12 +1844,16 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
if ( target_state = = PCI_POWER_ERROR )
return - EIO ;
dev - > runtime_d3cold = target_state = = PCI_D3cold ;
__pci_enable_wake ( dev , target_state , true , pci_dev_run_wake ( dev ) ) ;
error = pci_set_power_state ( dev , target_state ) ;
if ( error )
if ( error ) {
__pci_enable_wake ( dev , target_state , true , false ) ;
dev - > runtime_d3cold = false ;
}
return error ;
}
@ -1818,6 +1923,7 @@ void pci_pm_init(struct pci_dev *dev)
dev - > pm_cap = pm ;
dev - > d3_delay = PCI_PM_D3_WAIT ;
dev - > d3cold_delay = PCI_PM_D3COLD_WAIT ;
dev - > d1_support = false ;
dev - > d2_support = false ;