2005-04-16 15:20:36 -07:00
/*
* suspend . c - Functions for putting devices to sleep .
*
* Copyright ( c ) 2003 Patrick Mochel
* Copyright ( c ) 2003 Open Source Development Labs
*
* This file is released under the GPLv2
*
*/
# include <linux/device.h>
2006-03-23 01:38:34 -08:00
# include <linux/kallsyms.h>
# include <linux/pm.h>
2006-01-19 17:30:17 +01:00
# include "../base.h"
2005-04-16 15:20:36 -07:00
# include "power.h"
/*
* The entries in the dpm_active list are in a depth first order , simply
* because children are guaranteed to be discovered after parents , and
* are inserted at the back of the list on discovery .
*
* All list on the suspend path are done in reverse order , so we operate
* on the leaves of the device tree ( or forests , depending on how you want
* to look at it ; ) first . As nodes are removed from the back of the list ,
* they are inserted into the front of their destintation lists .
*
* Things are the reverse on the resume path - iterations are done in
* forward order , and nodes are inserted at the back of their destination
* lists . This way , the ancestors will be accessed before their descendents .
*/
2006-05-16 17:03:25 -07:00
static inline char * suspend_verb ( u32 event )
{
switch ( event ) {
case PM_EVENT_SUSPEND : return " suspend " ;
case PM_EVENT_FREEZE : return " freeze " ;
2006-08-14 23:11:08 -07:00
case PM_EVENT_PRETHAW : return " prethaw " ;
2006-05-16 17:03:25 -07:00
default : return " (unknown suspend event) " ;
}
}
2005-04-16 15:20:36 -07:00
2007-06-13 16:19:27 +02:00
static void
suspend_device_dbg ( struct device * dev , pm_message_t state , char * info )
{
dev_dbg ( dev , " %s%s%s \n " , info , suspend_verb ( state . event ) ,
( ( state . event = = PM_EVENT_SUSPEND ) & & device_may_wakeup ( dev ) ) ?
" , may wakeup " : " " ) ;
}
2005-04-16 15:20:36 -07:00
/**
* suspend_device - Save state of one device .
* @ dev : Device .
* @ state : Power state device is entering .
*/
int suspend_device ( struct device * dev , pm_message_t state )
{
int error = 0 ;
2005-03-21 10:41:04 -08:00
down ( & dev - > sem ) ;
2005-09-03 15:56:57 -07:00
if ( dev - > power . power_state . event ) {
2005-05-09 08:07:00 -07:00
dev_dbg ( dev , " PM: suspend %d-->%d \n " ,
2005-09-03 15:56:57 -07:00
dev - > power . power_state . event , state . event ) ;
2005-05-09 08:07:00 -07:00
}
2005-04-16 15:20:36 -07:00
2007-06-17 19:49:20 +02:00
if ( dev - > class & & dev - > class - > suspend ) {
2007-06-13 16:19:27 +02:00
suspend_device_dbg ( dev , state , " class " ) ;
2006-06-24 14:50:29 -07:00
error = dev - > class - > suspend ( dev , state ) ;
suspend_report_result ( dev - > class - > suspend , error ) ;
}
2007-06-17 19:49:20 +02:00
if ( ! error & & dev - > type & & dev - > type - > suspend ) {
2007-06-13 16:19:27 +02:00
suspend_device_dbg ( dev , state , " type " ) ;
2007-04-03 01:08:40 -04:00
error = dev - > type - > suspend ( dev , state ) ;
suspend_report_result ( dev - > type - > suspend , error ) ;
}
2007-06-17 19:49:20 +02:00
if ( ! error & & dev - > bus & & dev - > bus - > suspend ) {
2007-06-13 16:19:27 +02:00
suspend_device_dbg ( dev , state , " " ) ;
2005-04-16 15:20:36 -07:00
error = dev - > bus - > suspend ( dev , state ) ;
2006-03-23 01:38:34 -08:00
suspend_report_result ( dev - > bus - > suspend , error ) ;
2005-05-09 08:07:00 -07:00
}
2005-03-21 10:41:04 -08:00
up ( & dev - > sem ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
2006-05-30 21:26:03 -07:00
2006-06-24 14:50:29 -07:00
/*
* This is called with interrupts off , only a single CPU
2007-05-23 14:19:41 -07:00
* running . We can ' t acquire a mutex or semaphore ( and we don ' t
2006-06-24 14:50:29 -07:00
* need the protection )
*/
static int suspend_device_late ( struct device * dev , pm_message_t state )
{
int error = 0 ;
2007-06-17 19:49:20 +02:00
if ( dev - > bus & & dev - > bus - > suspend_late ) {
2007-06-13 16:19:27 +02:00
suspend_device_dbg ( dev , state , " LATE " ) ;
2006-06-24 14:50:29 -07:00
error = dev - > bus - > suspend_late ( dev , state ) ;
suspend_report_result ( dev - > bus - > suspend_late , error ) ;
}
return error ;
}
2005-04-16 15:20:36 -07:00
/**
* device_suspend - Save state and stop all devices in system .
* @ state : Power state to put each device in .
*
* Walk the dpm_active list , call - > suspend ( ) for each device , and move
2006-06-24 14:50:29 -07:00
* it to the dpm_off list .
*
* ( For historical reasons , if it returns - EAGAIN , that used to mean
* that the device would be called again with interrupts disabled .
* These days , we use the " suspend_late() " callback for that , so we
* print a warning and consider it an error ) .
*
* If we get a different error , try and back out .
2005-04-16 15:20:36 -07:00
*
* If we hit a failure with any of the devices , call device_resume ( )
* above to bring the suspended devices back to life .
*
*/
int device_suspend ( pm_message_t state )
{
int error = 0 ;
2006-08-31 22:02:11 -07:00
might_sleep ( ) ;
2007-05-23 14:19:41 -07:00
mutex_lock ( & dpm_mtx ) ;
mutex_lock ( & dpm_list_mtx ) ;
2005-04-16 15:20:36 -07:00
while ( ! list_empty ( & dpm_active ) & & error = = 0 ) {
struct list_head * entry = dpm_active . prev ;
struct device * dev = to_device ( entry ) ;
get_device ( dev ) ;
2007-05-23 14:19:41 -07:00
mutex_unlock ( & dpm_list_mtx ) ;
2005-04-16 15:20:36 -07:00
error = suspend_device ( dev , state ) ;
2007-05-23 14:19:41 -07:00
mutex_lock ( & dpm_list_mtx ) ;
2005-04-16 15:20:36 -07:00
/* Check if the device got removed */
if ( ! list_empty ( & dev - > power . entry ) ) {
2006-06-24 14:50:29 -07:00
/* Move it to the dpm_off list */
2006-06-26 00:24:40 -07:00
if ( ! error )
list_move ( & dev - > power . entry , & dpm_off ) ;
2005-04-16 15:20:36 -07:00
}
if ( error )
printk ( KERN_ERR " Could not suspend device %s: "
2006-06-24 14:50:29 -07:00
" error %d%s \n " ,
kobject_name ( & dev - > kobj ) , error ,
error = = - EAGAIN ? " (please convert to suspend_late) " : " " ) ;
2005-04-16 15:20:36 -07:00
put_device ( dev ) ;
}
2007-05-23 14:19:41 -07:00
mutex_unlock ( & dpm_list_mtx ) ;
2006-06-24 14:50:29 -07:00
if ( error )
2005-04-16 15:20:36 -07:00
dpm_resume ( ) ;
2006-06-24 14:50:29 -07:00
2007-05-23 14:19:41 -07:00
mutex_unlock ( & dpm_mtx ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
EXPORT_SYMBOL_GPL ( device_suspend ) ;
/**
* device_power_down - Shut down special devices .
* @ state : Power state to enter .
*
* Walk the dpm_off_irq list , calling - > power_down ( ) for each device that
* couldn ' t power down the device with interrupts enabled . When we ' re
* done , power down system devices .
*/
int device_power_down ( pm_message_t state )
{
int error = 0 ;
struct device * dev ;
2006-06-24 14:50:29 -07:00
while ( ! list_empty ( & dpm_off ) ) {
struct list_head * entry = dpm_off . prev ;
dev = to_device ( entry ) ;
error = suspend_device_late ( dev , state ) ;
if ( error )
goto Error ;
list_move ( & dev - > power . entry , & dpm_off_irq ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-24 14:50:29 -07:00
error = sysdev_suspend ( state ) ;
2005-04-16 15:20:36 -07:00
Done :
return error ;
Error :
printk ( KERN_ERR " Could not power down device %s: "
" error %d \n " , kobject_name ( & dev - > kobj ) , error ) ;
dpm_power_up ( ) ;
goto Done ;
}
EXPORT_SYMBOL_GPL ( device_power_down ) ;
2006-03-23 01:38:34 -08:00
void __suspend_report_result ( const char * function , void * fn , int ret )
{
if ( ret ) {
printk ( KERN_ERR " %s(): " , function ) ;
print_fn_descriptor_symbol ( " %s() returns " , ( unsigned long ) fn ) ;
printk ( " %d \n " , ret ) ;
}
}
EXPORT_SYMBOL_GPL ( __suspend_report_result ) ;