@ -32,6 +32,8 @@
# include "../base.h"
# include "power.h"
typedef int ( * pm_callback_t ) ( struct device * ) ;
/*
* The entries in the dpm_list list are in a depth first order , simply
* because children are guaranteed to be discovered after parents , and
@ -164,8 +166,9 @@ static ktime_t initcall_debug_start(struct device *dev)
ktime_t calltime = ktime_set ( 0 , 0 ) ;
if ( initcall_debug ) {
pr_info ( " calling %s+ @ %i \n " ,
dev_name ( dev ) , task_pid_nr ( current ) ) ;
pr_info ( " calling %s+ @ %i, parent: %s \n " ,
dev_name ( dev ) , task_pid_nr ( current ) ,
dev - > parent ? dev_name ( dev - > parent ) : " none " ) ;
calltime = ktime_get ( ) ;
}
@ -211,151 +214,69 @@ static void dpm_wait_for_children(struct device *dev, bool async)
}
/**
* pm_op - Execute the PM operation appropriate for given PM event .
* @ dev : Device to handle .
* pm_op - Return the PM operation appropriate for given PM event .
* @ ops : PM operations to choose from .
* @ state : PM transition of the system being carried out .
*/
static int pm_op ( struct device * dev ,
const struct dev_pm_ops * ops ,
pm_message_t state )
static pm_callback_t pm_op ( const struct dev_pm_ops * ops , pm_message_t state )
{
int error = 0 ;
ktime_t calltime ;
calltime = initcall_debug_start ( dev ) ;
switch ( state . event ) {
# ifdef CONFIG_SUSPEND
case PM_EVENT_SUSPEND :
if ( ops - > suspend ) {
error = ops - > suspend ( dev ) ;
suspend_report_result ( ops - > suspend , error ) ;
}
break ;
return ops - > suspend ;
case PM_EVENT_RESUME :
if ( ops - > resume ) {
error = ops - > resume ( dev ) ;
suspend_report_result ( ops - > resume , error ) ;
}
break ;
return ops - > resume ;
# endif /* CONFIG_SUSPEND */
# ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE :
case PM_EVENT_QUIESCE :
if ( ops - > freeze ) {
error = ops - > freeze ( dev ) ;
suspend_report_result ( ops - > freeze , error ) ;
}
break ;
return ops - > freeze ;
case PM_EVENT_HIBERNATE :
if ( ops - > poweroff ) {
error = ops - > poweroff ( dev ) ;
suspend_report_result ( ops - > poweroff , error ) ;
}
break ;
return ops - > poweroff ;
case PM_EVENT_THAW :
case PM_EVENT_RECOVER :
if ( ops - > thaw ) {
error = ops - > thaw ( dev ) ;
suspend_report_result ( ops - > thaw , error ) ;
}
return ops - > thaw ;
break ;
case PM_EVENT_RESTORE :
if ( ops - > restore ) {
error = ops - > restore ( dev ) ;
suspend_report_result ( ops - > restore , error ) ;
}
break ;
return ops - > restore ;
# endif /* CONFIG_HIBERNATE_CALLBACKS */
default :
error = - EINVAL ;
}
initcall_debug_report ( dev , calltime , error ) ;
return error ;
return NULL ;
}
/**
* pm_noirq_op - Execute the PM operation appropriate for given PM event .
* @ dev : Device to handle .
* pm_noirq_op - Return the PM operation appropriate for given PM event .
* @ ops : PM operations to choose from .
* @ state : PM transition of the system being carried out .
*
* The driver of @ dev will not receive interrupts while this function is being
* executed .
*/
static int pm_noirq_op ( struct device * dev ,
const struct dev_pm_ops * ops ,
pm_message_t state )
static pm_callback_t pm_noirq_op ( const struct dev_pm_ops * ops , pm_message_t state )
{
int error = 0 ;
ktime_t calltime = ktime_set ( 0 , 0 ) , delta , rettime ;
if ( initcall_debug ) {
pr_info ( " calling %s+ @ %i, parent: %s \n " ,
dev_name ( dev ) , task_pid_nr ( current ) ,
dev - > parent ? dev_name ( dev - > parent ) : " none " ) ;
calltime = ktime_get ( ) ;
}
switch ( state . event ) {
# ifdef CONFIG_SUSPEND
case PM_EVENT_SUSPEND :
if ( ops - > suspend_noirq ) {
error = ops - > suspend_noirq ( dev ) ;
suspend_report_result ( ops - > suspend_noirq , error ) ;
}
break ;
return ops - > suspend_noirq ;
case PM_EVENT_RESUME :
if ( ops - > resume_noirq ) {
error = ops - > resume_noirq ( dev ) ;
suspend_report_result ( ops - > resume_noirq , error ) ;
}
break ;
return ops - > resume_noirq ;
# endif /* CONFIG_SUSPEND */
# ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE :
case PM_EVENT_QUIESCE :
if ( ops - > freeze_noirq ) {
error = ops - > freeze_noirq ( dev ) ;
suspend_report_result ( ops - > freeze_noirq , error ) ;
}
break ;
return ops - > freeze_noirq ;
case PM_EVENT_HIBERNATE :
if ( ops - > poweroff_noirq ) {
error = ops - > poweroff_noirq ( dev ) ;
suspend_report_result ( ops - > poweroff_noirq , error ) ;
}
break ;
return ops - > poweroff_noirq ;
case PM_EVENT_THAW :
case PM_EVENT_RECOVER :
if ( ops - > thaw_noirq ) {
error = ops - > thaw_noirq ( dev ) ;
suspend_report_result ( ops - > thaw_noirq , error ) ;
}
break ;
return ops - > thaw_noirq ;
case PM_EVENT_RESTORE :
if ( ops - > restore_noirq ) {
error = ops - > restore_noirq ( dev ) ;
suspend_report_result ( ops - > restore_noirq , error ) ;
}
break ;
return ops - > restore_noirq ;
# endif /* CONFIG_HIBERNATE_CALLBACKS */
default :
error = - EINVAL ;
}
if ( initcall_debug ) {
rettime = ktime_get ( ) ;
delta = ktime_sub ( rettime , calltime ) ;
printk ( " initcall %s_i+ returned %d after %Ld usecs \n " ,
dev_name ( dev ) , error ,
( unsigned long long ) ktime_to_ns ( delta ) > > 10 ) ;
}
return error ;
return NULL ;
}
static char * pm_verb ( int event )
@ -413,6 +334,26 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)
usecs / USEC_PER_MSEC , usecs % USEC_PER_MSEC ) ;
}
static int dpm_run_callback ( pm_callback_t cb , struct device * dev ,
pm_message_t state , char * info )
{
ktime_t calltime ;
int error ;
if ( ! cb )
return 0 ;
calltime = initcall_debug_start ( dev ) ;
pm_dev_dbg ( dev , state , info ) ;
error = cb ( dev ) ;
suspend_report_result ( cb , error ) ;
initcall_debug_report ( dev , calltime , error ) ;
return error ;
}
/*------------------------- Resume routines -------------------------*/
/**
@ -425,25 +366,34 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)
*/
static int device_resume_noirq ( struct device * dev , pm_message_t state )
{
pm_callback_t callback = NULL ;
char * info = NULL ;
int error = 0 ;
TRACE_DEVICE ( dev ) ;
TRACE_RESUME ( 0 ) ;
if ( dev - > pm_domain ) {
pm_dev_dbg ( dev , state , " EARLY power domain " ) ;
error = pm_noirq_op ( dev , & dev - > pm_domain - > ops , state ) ;
info = " EARLY power domain " ;
callback = pm_noirq_op ( & dev - > pm_domain - > ops , state ) ;
} else if ( dev - > type & & dev - > type - > pm ) {
pm_dev_dbg ( dev , state , " EARLY type " ) ;
error = pm_noirq_op ( dev , dev - > type - > pm , state ) ;
info = " EARLY type " ;
callback = pm_noirq_op ( dev - > type - > pm , state ) ;
} else if ( dev - > class & & dev - > class - > pm ) {
pm_dev_dbg ( dev , state , " EARLY class " ) ;
error = pm_noirq_op ( dev , dev - > class - > pm , state ) ;
info = " EARLY class " ;
callback = pm_noirq_op ( dev - > class - > pm , state ) ;
} else if ( dev - > bus & & dev - > bus - > pm ) {
pm_dev_dbg ( dev , state , " EARLY " ) ;
error = pm_noirq_op ( dev , dev - > bus - > pm , state ) ;
info = " EARLY bus " ;
callback = pm_noirq_op ( dev - > bus - > pm , state ) ;
}
if ( ! callback & & dev - > driver & & dev - > driver - > pm ) {
info = " EARLY driver " ;
callback = pm_noirq_op ( dev - > driver - > pm , state ) ;
}
error = dpm_run_callback ( callback , dev , state , info ) ;
TRACE_RESUME ( error ) ;
return error ;
}
@ -485,26 +435,6 @@ void dpm_resume_noirq(pm_message_t state)
}
EXPORT_SYMBOL_GPL ( dpm_resume_noirq ) ;
/**
* legacy_resume - Execute a legacy ( bus or class ) resume callback for device .
* @ dev : Device to resume .
* @ cb : Resume callback to execute .
*/
static int legacy_resume ( struct device * dev , int ( * cb ) ( struct device * dev ) )
{
int error ;
ktime_t calltime ;
calltime = initcall_debug_start ( dev ) ;
error = cb ( dev ) ;
suspend_report_result ( cb , error ) ;
initcall_debug_report ( dev , calltime , error ) ;
return error ;
}
/**
* device_resume - Execute " resume " callbacks for given device .
* @ dev : Device to handle .
@ -513,6 +443,8 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
*/
static int device_resume ( struct device * dev , pm_message_t state , bool async )
{
pm_callback_t callback = NULL ;
char * info = NULL ;
int error = 0 ;
bool put = false ;
@ -535,40 +467,48 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
put = true ;
if ( dev - > pm_domain ) {
pm_dev_dbg ( dev , state , " power domain " ) ;
error = pm_op ( dev , & dev - > pm_domain - > ops , state ) ;
goto End ;
info = " power domain " ;
callback = pm_op ( & dev - > pm_domain - > ops , state ) ;
goto Driver ;
}
if ( dev - > type & & dev - > type - > pm ) {
pm_dev_dbg ( dev , state , " type " ) ;
error = pm_op ( dev , dev - > type - > pm , state ) ;
goto End ;
info = " type " ;
callback = pm_op ( dev - > type - > pm , state ) ;
goto Driver ;
}
if ( dev - > class ) {
if ( dev - > class - > pm ) {
pm_dev_dbg ( dev , state , " class " ) ;
error = pm_op ( dev , dev - > class - > pm , state ) ;
goto End ;
info = " class " ;
callback = pm_op ( dev - > class - > pm , state ) ;
goto Driver ;
} else if ( dev - > class - > resume ) {
pm_dev_dbg ( dev , state , " legacy class " ) ;
error = legacy_resume ( dev , dev - > class - > resume ) ;
info = " legacy class " ;
callback = dev - > class - > resume ;
goto End ;
}
}
if ( dev - > bus ) {
if ( dev - > bus - > pm ) {
pm_dev_dbg ( dev , state , " " ) ;
error = pm_op ( dev , dev - > bus - > pm , state ) ;
info = " bus " ;
callback = pm_op ( dev - > bus - > pm , state ) ;
} else if ( dev - > bus - > resume ) {
pm_dev_dbg ( dev , state , " legacy " ) ;
error = legacy_resume ( dev , dev - > bus - > resume ) ;
info = " legacy bus " ;
callback = dev - > bus - > resume ;
goto End ;
}
}
Driver :
if ( ! callback & & dev - > driver & & dev - > driver - > pm ) {
info = " driver " ;
callback = pm_op ( dev - > driver - > pm , state ) ;
}
End :
error = dpm_run_callback ( callback , dev , state , info ) ;
dev - > power . is_suspended = false ;
Unlock :
@ -660,24 +600,33 @@ void dpm_resume(pm_message_t state)
*/
static void device_complete ( struct device * dev , pm_message_t state )
{
void ( * callback ) ( struct device * ) = NULL ;
char * info = NULL ;
device_lock ( dev ) ;
if ( dev - > pm_domain ) {
pm_dev_dbg ( dev , state , " completing power domain " ) ;
if ( dev - > pm_domain - > ops . complete )
dev - > pm_domain - > ops . complete ( dev ) ;
info = " completing power domain " ;
callback = dev - > pm_domain - > ops . complete ;
} else if ( dev - > type & & dev - > type - > pm ) {
pm_dev_dbg ( dev , state , " completing type " ) ;
if ( dev - > type - > pm - > complete )
dev - > type - > pm - > complete ( dev ) ;
info = " completing type " ;
callback = dev - > type - > pm - > complete ;
} else if ( dev - > class & & dev - > class - > pm ) {
pm_dev_dbg ( dev , state , " completing class " ) ;
if ( dev - > class - > pm - > complete )
dev - > class - > pm - > complete ( dev ) ;
info = " completing class " ;
callback = dev - > class - > pm - > complete ;
} else if ( dev - > bus & & dev - > bus - > pm ) {
pm_dev_dbg ( dev , state , " completing " ) ;
if ( dev - > bus - > pm - > complete )
dev - > bus - > pm - > complete ( dev ) ;
info = " completing bus " ;
callback = dev - > bus - > pm - > complete ;
}
if ( ! callback & & dev - > driver & & dev - > driver - > pm ) {
info = " completing driver " ;
callback = dev - > driver - > pm - > complete ;
}
if ( callback ) {
pm_dev_dbg ( dev , state , info ) ;
callback ( dev ) ;
}
device_unlock ( dev ) ;
@ -763,31 +712,29 @@ static pm_message_t resume_event(pm_message_t sleep_state)
*/
static int device_suspend_noirq ( struct device * dev , pm_message_t state )
{
int error ;
pm_callback_t callback = NULL ;
char * info = NULL ;
if ( dev - > pm_domain ) {
pm_dev_dbg ( dev , state , " LATE power domain " ) ;
error = pm_noirq_op ( dev , & dev - > pm_domain - > ops , state ) ;
if ( error )
return error ;
info = " LATE power domain " ;
callback = pm_noirq_op ( & dev - > pm_domain - > ops , state ) ;
} else if ( dev - > type & & dev - > type - > pm ) {
pm_dev_dbg ( dev , state , " LATE type " ) ;
error = pm_noirq_op ( dev , dev - > type - > pm , state ) ;
if ( error )
return error ;
info = " LATE type " ;
callback = pm_noirq_op ( dev - > type - > pm , state ) ;
} else if ( dev - > class & & dev - > class - > pm ) {
pm_dev_dbg ( dev , state , " LATE class " ) ;
error = pm_noirq_op ( dev , dev - > class - > pm , state ) ;
if ( error )
return error ;
info = " LATE class " ;
callback = pm_noirq_op ( dev - > class - > pm , state ) ;
} else if ( dev - > bus & & dev - > bus - > pm ) {
pm_dev_dbg ( dev , state , " LATE " ) ;
error = pm_noirq_op ( dev , dev - > bus - > pm , state ) ;
if ( error )
return error ;
info = " LATE bus " ;
callback = pm_noirq_op ( dev - > bus - > pm , state ) ;
}
return 0 ;
if ( ! callback & & dev - > driver & & dev - > driver - > pm ) {
info = " LATE driver " ;
callback = pm_noirq_op ( dev - > driver - > pm , state ) ;
}
return dpm_run_callback ( callback , dev , state , info ) ;
}
/**
@ -864,6 +811,8 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
*/
static int __device_suspend ( struct device * dev , pm_message_t state , bool async )
{
pm_callback_t callback = NULL ;
char * info = NULL ;
int error = 0 ;
dpm_wait_for_children ( dev , async ) ;
@ -884,22 +833,22 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
device_lock ( dev ) ;
if ( dev - > pm_domain ) {
pm_dev_dbg ( dev , state , " power domain " ) ;
error = pm_op ( dev , & dev - > pm_domain - > ops , state ) ;
goto End ;
info = " power domain " ;
callback = pm_op ( & dev - > pm_domain - > ops , state ) ;
goto Run ;
}
if ( dev - > type & & dev - > type - > pm ) {
pm_dev_dbg ( dev , state , " type " ) ;
error = pm_op ( dev , dev - > type - > pm , state ) ;
goto End ;
info = " type " ;
callback = pm_op ( dev - > type - > pm , state ) ;
goto Run ;
}
if ( dev - > class ) {
if ( dev - > class - > pm ) {
pm_dev_dbg ( dev , state , " class " ) ;
error = pm_op ( dev , dev - > class - > pm , state ) ;
goto End ;
info = " class " ;
callback = pm_op ( dev - > class - > pm , state ) ;
goto Run ;
} else if ( dev - > class - > suspend ) {
pm_dev_dbg ( dev , state , " legacy class " ) ;
error = legacy_suspend ( dev , state , dev - > class - > suspend ) ;
@ -909,14 +858,23 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if ( dev - > bus ) {
if ( dev - > bus - > pm ) {
pm_dev_dbg ( dev , state , " " ) ;
error = pm_op ( dev , dev - > bus - > pm , state ) ;
info = " bus " ;
callback = pm_op ( dev - > bus - > pm , state ) ;
} else if ( dev - > bus - > suspend ) {
pm_dev_dbg ( dev , state , " legacy " ) ;
pm_dev_dbg ( dev , state , " legacy bus " ) ;
error = legacy_suspend ( dev , state , dev - > bus - > suspend ) ;
goto End ;
}
}
Run :
if ( ! callback & & dev - > driver & & dev - > driver - > pm ) {
info = " driver " ;
callback = pm_op ( dev - > driver - > pm , state ) ;
}
error = dpm_run_callback ( callback , dev , state , info ) ;
End :
if ( ! error ) {
dev - > power . is_suspended = true ;
@ -1022,6 +980,8 @@ int dpm_suspend(pm_message_t state)
*/
static int device_prepare ( struct device * dev , pm_message_t state )
{
int ( * callback ) ( struct device * ) = NULL ;
char * info = NULL ;
int error = 0 ;
device_lock ( dev ) ;
@ -1029,34 +989,29 @@ static int device_prepare(struct device *dev, pm_message_t state)
dev - > power . wakeup_path = device_may_wakeup ( dev ) ;
if ( dev - > pm_domain ) {
pm_dev_dbg ( dev , state , " preparing power domain " ) ;
if ( dev - > pm_domain - > ops . prepare )
error = dev - > pm_domain - > ops . prepare ( dev ) ;
suspend_report_result ( dev - > pm_domain - > ops . prepare , error ) ;
if ( error )
goto End ;
info = " preparing power domain " ;
callback = dev - > pm_domain - > ops . prepare ;
} else if ( dev - > type & & dev - > type - > pm ) {
pm_dev_dbg ( dev , state , " preparing type " ) ;
if ( dev - > type - > pm - > prepare )
error = dev - > type - > pm - > prepare ( dev ) ;
suspend_report_result ( dev - > type - > pm - > prepare , error ) ;
if ( error )
goto End ;
info = " preparing type " ;
callback = dev - > type - > pm - > prepare ;
} else if ( dev - > class & & dev - > class - > pm ) {
pm_dev_dbg ( dev , state , " preparing class " ) ;
if ( dev - > class - > pm - > prepare )
error = dev - > class - > pm - > prepare ( dev ) ;
suspend_report_result ( dev - > class - > pm - > prepare , error ) ;
if ( error )
goto End ;
info = " preparing class " ;
callback = dev - > class - > pm - > prepare ;
} else if ( dev - > bus & & dev - > bus - > pm ) {
pm_dev_dbg ( dev , state , " preparing " ) ;
if ( dev - > bus - > pm - > prepare )
error = dev - > bus - > pm - > prepare ( dev ) ;
suspend_report_result ( dev - > bus - > pm - > prepare , error ) ;
info = " preparing bus " ;
callback = dev - > bus - > pm - > prepare ;
}
if ( ! callback & & dev - > driver & & dev - > driver - > pm ) {
info = " preparing driver " ;
callback = dev - > driver - > pm - > prepare ;
}
if ( callback ) {
error = callback ( dev ) ;
suspend_report_result ( callback , error ) ;
}
End :
device_unlock ( dev ) ;
return error ;