2011-04-30 00:25:44 +02:00
/*
* drivers / base / power / clock_ops . c - Generic clock manipulation PM callbacks
*
* Copyright ( c ) 2011 Rafael J . Wysocki < rjw @ sisk . pl > , Renesas Electronics Corp .
*
* This file is released under the GPLv2 .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/io.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/clk.h>
# include <linux/slab.h>
# include <linux/err.h>
2011-07-01 22:13:37 +02:00
# ifdef CONFIG_PM
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
struct pm_clk_data {
2011-04-30 00:25:44 +02:00
struct list_head clock_list ;
struct mutex lock ;
} ;
enum pce_status {
PCE_STATUS_NONE = 0 ,
PCE_STATUS_ACQUIRED ,
PCE_STATUS_ENABLED ,
PCE_STATUS_ERROR ,
} ;
struct pm_clock_entry {
struct list_head node ;
char * con_id ;
struct clk * clk ;
enum pce_status status ;
} ;
2011-07-01 22:13:44 +02:00
static struct pm_clk_data * __to_pcd ( struct device * dev )
2011-04-30 00:25:44 +02:00
{
return dev ? dev - > power . subsys_data : NULL ;
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_add - Start using a device clock for power management .
* @ dev : Device whose clock is going to be used for power management .
2011-04-30 00:25:44 +02:00
* @ con_id : Connection ID of the clock .
*
* Add the clock represented by @ con_id to the list of clocks used for
2011-07-01 22:13:44 +02:00
* the power management of @ dev .
2011-04-30 00:25:44 +02:00
*/
2011-07-01 22:13:44 +02:00
int pm_clk_add ( struct device * dev , const char * con_id )
2011-04-30 00:25:44 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
2011-07-01 22:13:44 +02:00
if ( ! pcd )
2011-04-30 00:25:44 +02:00
return - EINVAL ;
ce = kzalloc ( sizeof ( * ce ) , GFP_KERNEL ) ;
if ( ! ce ) {
dev_err ( dev , " Not enough memory for clock entry. \n " ) ;
return - ENOMEM ;
}
if ( con_id ) {
ce - > con_id = kstrdup ( con_id , GFP_KERNEL ) ;
if ( ! ce - > con_id ) {
dev_err ( dev ,
" Not enough memory for clock connection ID. \n " ) ;
kfree ( ce ) ;
return - ENOMEM ;
}
}
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
list_add_tail ( & ce - > node , & pcd - > clock_list ) ;
mutex_unlock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
return 0 ;
}
/**
2011-07-01 22:13:44 +02:00
* __pm_clk_remove - Destroy PM clock entry .
* @ ce : PM clock entry to destroy .
2011-04-30 00:25:44 +02:00
*
2011-07-01 22:13:44 +02:00
* This routine must be called under the mutex protecting the PM list of clocks
* corresponding the the @ ce ' s device .
2011-04-30 00:25:44 +02:00
*/
2011-07-01 22:13:44 +02:00
static void __pm_clk_remove ( struct pm_clock_entry * ce )
2011-04-30 00:25:44 +02:00
{
if ( ! ce )
return ;
list_del ( & ce - > node ) ;
if ( ce - > status < PCE_STATUS_ERROR ) {
if ( ce - > status = = PCE_STATUS_ENABLED )
clk_disable ( ce - > clk ) ;
if ( ce - > status > = PCE_STATUS_ACQUIRED )
clk_put ( ce - > clk ) ;
}
if ( ce - > con_id )
kfree ( ce - > con_id ) ;
kfree ( ce ) ;
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_remove - Stop using a device clock for power management .
* @ dev : Device whose clock should not be used for PM any more .
2011-04-30 00:25:44 +02:00
* @ con_id : Connection ID of the clock .
*
* Remove the clock represented by @ con_id from the list of clocks used for
2011-07-01 22:13:44 +02:00
* the power management of @ dev .
2011-04-30 00:25:44 +02:00
*/
2011-07-01 22:13:44 +02:00
void pm_clk_remove ( struct device * dev , const char * con_id )
2011-04-30 00:25:44 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
2011-07-01 22:13:44 +02:00
if ( ! pcd )
2011-04-30 00:25:44 +02:00
return ;
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
list_for_each_entry ( ce , & pcd - > clock_list , node ) {
2011-04-30 00:25:44 +02:00
if ( ! con_id & & ! ce - > con_id ) {
2011-07-01 22:13:44 +02:00
__pm_clk_remove ( ce ) ;
2011-04-30 00:25:44 +02:00
break ;
} else if ( ! con_id | | ! ce - > con_id ) {
continue ;
} else if ( ! strcmp ( con_id , ce - > con_id ) ) {
2011-07-01 22:13:44 +02:00
__pm_clk_remove ( ce ) ;
2011-04-30 00:25:44 +02:00
break ;
}
}
2011-07-01 22:13:44 +02:00
mutex_unlock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_init - Initialize a device ' s list of power management clocks .
* @ dev : Device to initialize the list of PM clocks for .
2011-04-30 00:25:44 +02:00
*
2011-07-01 22:13:44 +02:00
* Allocate a struct pm_clk_data object , initialize its lock member and
2011-04-30 00:25:44 +02:00
* make the @ dev ' s power . subsys_data field point to it .
*/
2011-07-01 22:13:44 +02:00
int pm_clk_init ( struct device * dev )
2011-04-30 00:25:44 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
pcd = kzalloc ( sizeof ( * pcd ) , GFP_KERNEL ) ;
if ( ! pcd ) {
dev_err ( dev , " Not enough memory for PM clock data. \n " ) ;
2011-04-30 00:25:44 +02:00
return - ENOMEM ;
}
2011-07-01 22:13:44 +02:00
INIT_LIST_HEAD ( & pcd - > clock_list ) ;
mutex_init ( & pcd - > lock ) ;
dev - > power . subsys_data = pcd ;
2011-04-30 00:25:44 +02:00
return 0 ;
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_destroy - Destroy a device ' s list of power management clocks .
* @ dev : Device to destroy the list of PM clocks for .
2011-04-30 00:25:44 +02:00
*
* Clear the @ dev ' s power . subsys_data field , remove the list of clock entries
2011-07-01 22:13:44 +02:00
* from the struct pm_clk_data object pointed to by it before and free
2011-04-30 00:25:44 +02:00
* that object .
*/
2011-07-01 22:13:44 +02:00
void pm_clk_destroy ( struct device * dev )
2011-04-30 00:25:44 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce , * c ;
2011-07-01 22:13:44 +02:00
if ( ! pcd )
2011-04-30 00:25:44 +02:00
return ;
dev - > power . subsys_data = NULL ;
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
list_for_each_entry_safe_reverse ( ce , c , & pcd - > clock_list , node )
__pm_clk_remove ( ce ) ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
mutex_unlock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
kfree ( pcd ) ;
2011-04-30 00:25:44 +02:00
}
2011-07-01 22:13:37 +02:00
# endif /* CONFIG_PM */
# ifdef CONFIG_PM_RUNTIME
2011-04-30 00:25:44 +02:00
/**
2011-07-01 22:13:44 +02:00
* pm_clk_acquire - Acquire a device clock .
2011-04-30 00:25:44 +02:00
* @ dev : Device whose clock is to be acquired .
* @ con_id : Connection ID of the clock .
*/
2011-07-01 22:13:44 +02:00
static void pm_clk_acquire ( struct device * dev ,
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce )
{
ce - > clk = clk_get ( dev , ce - > con_id ) ;
if ( IS_ERR ( ce - > clk ) ) {
ce - > status = PCE_STATUS_ERROR ;
} else {
ce - > status = PCE_STATUS_ACQUIRED ;
dev_dbg ( dev , " Clock %s managed by runtime PM. \n " , ce - > con_id ) ;
}
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_suspend - Disable clocks in a device ' s PM clock list .
2011-04-30 00:25:44 +02:00
* @ dev : Device to disable the clocks for .
*/
2011-07-01 22:13:44 +02:00
int pm_clk_suspend ( struct device * dev )
2011-04-30 00:25:44 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
dev_dbg ( dev , " %s() \n " , __func__ ) ;
2011-07-01 22:13:44 +02:00
if ( ! pcd )
2011-04-30 00:25:44 +02:00
return 0 ;
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
list_for_each_entry_reverse ( ce , & pcd - > clock_list , node ) {
2011-04-30 00:25:44 +02:00
if ( ce - > status = = PCE_STATUS_NONE )
2011-07-01 22:13:44 +02:00
pm_clk_acquire ( dev , ce ) ;
2011-04-30 00:25:44 +02:00
if ( ce - > status < PCE_STATUS_ERROR ) {
clk_disable ( ce - > clk ) ;
ce - > status = PCE_STATUS_ACQUIRED ;
}
}
2011-07-01 22:13:44 +02:00
mutex_unlock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
return 0 ;
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_resume - Enable clocks in a device ' s PM clock list .
2011-04-30 00:25:44 +02:00
* @ dev : Device to enable the clocks for .
*/
2011-07-01 22:13:44 +02:00
int pm_clk_resume ( struct device * dev )
2011-04-30 00:25:44 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
dev_dbg ( dev , " %s() \n " , __func__ ) ;
2011-07-01 22:13:44 +02:00
if ( ! pcd )
2011-04-30 00:25:44 +02:00
return 0 ;
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-07-01 22:13:44 +02:00
list_for_each_entry ( ce , & pcd - > clock_list , node ) {
2011-04-30 00:25:44 +02:00
if ( ce - > status = = PCE_STATUS_NONE )
2011-07-01 22:13:44 +02:00
pm_clk_acquire ( dev , ce ) ;
2011-04-30 00:25:44 +02:00
if ( ce - > status < PCE_STATUS_ERROR ) {
clk_enable ( ce - > clk ) ;
ce - > status = PCE_STATUS_ENABLED ;
}
}
2011-07-01 22:13:44 +02:00
mutex_unlock ( & pcd - > lock ) ;
2011-04-30 00:25:44 +02:00
return 0 ;
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_notify - Notify routine for device addition and removal .
2011-04-30 00:25:44 +02:00
* @ nb : Notifier block object this function is a member of .
* @ action : Operation being carried out by the caller .
* @ data : Device the routine is being run for .
*
* For this function to work , @ nb must be a member of an object of type
* struct pm_clk_notifier_block containing all of the requisite data .
2011-06-23 01:52:55 +02:00
* Specifically , the pm_domain member of that object is copied to the device ' s
* pm_domain field and its con_ids member is used to populate the device ' s list
2011-07-01 22:13:44 +02:00
* of PM clocks , depending on @ action .
2011-04-30 00:25:44 +02:00
*
2011-06-23 01:52:55 +02:00
* If the device ' s pm_domain field is already populated with a value different
2011-04-30 00:25:44 +02:00
* from the one stored in the struct pm_clk_notifier_block object , the function
* does nothing .
*/
2011-07-01 22:13:44 +02:00
static int pm_clk_notify ( struct notifier_block * nb ,
2011-04-30 00:25:44 +02:00
unsigned long action , void * data )
{
struct pm_clk_notifier_block * clknb ;
struct device * dev = data ;
2011-06-07 23:34:58 +02:00
char * * con_id ;
2011-04-30 00:25:44 +02:00
int error ;
dev_dbg ( dev , " %s() %ld \n " , __func__ , action ) ;
clknb = container_of ( nb , struct pm_clk_notifier_block , nb ) ;
switch ( action ) {
case BUS_NOTIFY_ADD_DEVICE :
2011-06-23 01:52:55 +02:00
if ( dev - > pm_domain )
2011-04-30 00:25:44 +02:00
break ;
2011-07-01 22:13:44 +02:00
error = pm_clk_init ( dev ) ;
2011-04-30 00:25:44 +02:00
if ( error )
break ;
2011-06-23 01:52:55 +02:00
dev - > pm_domain = clknb - > pm_domain ;
2011-04-30 00:25:44 +02:00
if ( clknb - > con_ids [ 0 ] ) {
2011-06-07 23:34:58 +02:00
for ( con_id = clknb - > con_ids ; * con_id ; con_id + + )
2011-07-01 22:13:44 +02:00
pm_clk_add ( dev , * con_id ) ;
2011-04-30 00:25:44 +02:00
} else {
2011-07-01 22:13:44 +02:00
pm_clk_add ( dev , NULL ) ;
2011-04-30 00:25:44 +02:00
}
break ;
case BUS_NOTIFY_DEL_DEVICE :
2011-06-23 01:52:55 +02:00
if ( dev - > pm_domain ! = clknb - > pm_domain )
2011-04-30 00:25:44 +02:00
break ;
2011-06-23 01:52:55 +02:00
dev - > pm_domain = NULL ;
2011-07-01 22:13:44 +02:00
pm_clk_destroy ( dev ) ;
2011-04-30 00:25:44 +02:00
break ;
}
return 0 ;
}
# else /* !CONFIG_PM_RUNTIME */
2011-07-01 22:13:37 +02:00
# ifdef CONFIG_PM
/**
2011-07-01 22:13:44 +02:00
* pm_clk_suspend - Disable clocks in a device ' s PM clock list .
2011-07-01 22:13:37 +02:00
* @ dev : Device to disable the clocks for .
*/
2011-07-01 22:13:44 +02:00
int pm_clk_suspend ( struct device * dev )
2011-07-01 22:13:37 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-07-01 22:13:37 +02:00
struct pm_clock_entry * ce ;
dev_dbg ( dev , " %s() \n " , __func__ ) ;
/* If there is no driver, the clocks are already disabled. */
2011-07-01 22:13:44 +02:00
if ( ! pcd | | ! dev - > driver )
2011-07-01 22:13:37 +02:00
return 0 ;
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
2011-07-01 22:13:37 +02:00
2011-07-01 22:13:44 +02:00
list_for_each_entry_reverse ( ce , & pcd - > clock_list , node )
2011-07-01 22:13:37 +02:00
clk_disable ( ce - > clk ) ;
2011-07-01 22:13:44 +02:00
mutex_unlock ( & pcd - > lock ) ;
2011-07-01 22:13:37 +02:00
return 0 ;
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_resume - Enable clocks in a device ' s PM clock list .
2011-07-01 22:13:37 +02:00
* @ dev : Device to enable the clocks for .
*/
2011-07-01 22:13:44 +02:00
int pm_clk_resume ( struct device * dev )
2011-07-01 22:13:37 +02:00
{
2011-07-01 22:13:44 +02:00
struct pm_clk_data * pcd = __to_pcd ( dev ) ;
2011-07-01 22:13:37 +02:00
struct pm_clock_entry * ce ;
dev_dbg ( dev , " %s() \n " , __func__ ) ;
/* If there is no driver, the clocks should remain disabled. */
2011-07-01 22:13:44 +02:00
if ( ! pcd | | ! dev - > driver )
2011-07-01 22:13:37 +02:00
return 0 ;
2011-07-01 22:13:44 +02:00
mutex_lock ( & pcd - > lock ) ;
2011-07-01 22:13:37 +02:00
2011-07-01 22:13:44 +02:00
list_for_each_entry ( ce , & pcd - > clock_list , node )
2011-07-01 22:13:37 +02:00
clk_enable ( ce - > clk ) ;
2011-07-01 22:13:44 +02:00
mutex_unlock ( & pcd - > lock ) ;
2011-07-01 22:13:37 +02:00
return 0 ;
}
# endif /* CONFIG_PM */
2011-04-30 00:25:44 +02:00
/**
* enable_clock - Enable a device clock .
* @ dev : Device whose clock is to be enabled .
* @ con_id : Connection ID of the clock .
*/
static void enable_clock ( struct device * dev , const char * con_id )
{
struct clk * clk ;
clk = clk_get ( dev , con_id ) ;
if ( ! IS_ERR ( clk ) ) {
clk_enable ( clk ) ;
clk_put ( clk ) ;
dev_info ( dev , " Runtime PM disabled, clock forced on. \n " ) ;
}
}
/**
* disable_clock - Disable a device clock .
* @ dev : Device whose clock is to be disabled .
* @ con_id : Connection ID of the clock .
*/
static void disable_clock ( struct device * dev , const char * con_id )
{
struct clk * clk ;
clk = clk_get ( dev , con_id ) ;
if ( ! IS_ERR ( clk ) ) {
clk_disable ( clk ) ;
clk_put ( clk ) ;
dev_info ( dev , " Runtime PM disabled, clock forced off. \n " ) ;
}
}
/**
2011-07-01 22:13:44 +02:00
* pm_clk_notify - Notify routine for device addition and removal .
2011-04-30 00:25:44 +02:00
* @ nb : Notifier block object this function is a member of .
* @ action : Operation being carried out by the caller .
* @ data : Device the routine is being run for .
*
* For this function to work , @ nb must be a member of an object of type
* struct pm_clk_notifier_block containing all of the requisite data .
* Specifically , the con_ids member of that object is used to enable or disable
* the device ' s clocks , depending on @ action .
*/
2011-07-01 22:13:44 +02:00
static int pm_clk_notify ( struct notifier_block * nb ,
2011-04-30 00:25:44 +02:00
unsigned long action , void * data )
{
struct pm_clk_notifier_block * clknb ;
struct device * dev = data ;
2011-06-07 23:34:58 +02:00
char * * con_id ;
2011-04-30 00:25:44 +02:00
dev_dbg ( dev , " %s() %ld \n " , __func__ , action ) ;
clknb = container_of ( nb , struct pm_clk_notifier_block , nb ) ;
switch ( action ) {
2011-06-21 23:24:33 +02:00
case BUS_NOTIFY_BIND_DRIVER :
2011-04-30 00:25:44 +02:00
if ( clknb - > con_ids [ 0 ] ) {
2011-06-07 23:34:58 +02:00
for ( con_id = clknb - > con_ids ; * con_id ; con_id + + )
enable_clock ( dev , * con_id ) ;
2011-04-30 00:25:44 +02:00
} else {
enable_clock ( dev , NULL ) ;
}
break ;
2011-06-21 23:24:33 +02:00
case BUS_NOTIFY_UNBOUND_DRIVER :
2011-04-30 00:25:44 +02:00
if ( clknb - > con_ids [ 0 ] ) {
2011-06-07 23:34:58 +02:00
for ( con_id = clknb - > con_ids ; * con_id ; con_id + + )
disable_clock ( dev , * con_id ) ;
2011-04-30 00:25:44 +02:00
} else {
disable_clock ( dev , NULL ) ;
}
break ;
}
return 0 ;
}
# endif /* !CONFIG_PM_RUNTIME */
/**
2011-07-01 22:13:44 +02:00
* pm_clk_add_notifier - Add bus type notifier for power management clocks .
2011-04-30 00:25:44 +02:00
* @ bus : Bus type to add the notifier to .
* @ clknb : Notifier to be added to the given bus type .
*
* The nb member of @ clknb is not expected to be initialized and its
2011-07-01 22:13:44 +02:00
* notifier_call member will be replaced with pm_clk_notify ( ) . However ,
2011-04-30 00:25:44 +02:00
* the remaining members of @ clknb should be populated prior to calling this
* routine .
*/
2011-07-01 22:13:44 +02:00
void pm_clk_add_notifier ( struct bus_type * bus ,
2011-04-30 00:25:44 +02:00
struct pm_clk_notifier_block * clknb )
{
if ( ! bus | | ! clknb )
return ;
2011-07-01 22:13:44 +02:00
clknb - > nb . notifier_call = pm_clk_notify ;
2011-04-30 00:25:44 +02:00
bus_register_notifier ( bus , & clknb - > nb ) ;
}