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/kernel.h>
2012-01-22 11:23:42 -05:00
# include <linux/device.h>
2011-04-30 00:25:44 +02:00
# include <linux/io.h>
# include <linux/pm.h>
2011-08-25 15:34:19 +02:00
# include <linux/pm_clock.h>
2011-04-30 00:25:44 +02:00
# include <linux/clk.h>
2014-11-06 15:51:00 +02:00
# include <linux/clkdev.h>
2011-04-30 00:25:44 +02:00
# 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
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 ;
} ;
2014-01-14 12:23:42 +00:00
/**
* pm_clk_enable - Enable a clock , reporting any errors
* @ dev : The device for the given clock
2014-11-06 15:51:01 +02:00
* @ ce : PM clock entry corresponding to the clock .
2014-01-14 12:23:42 +00:00
*/
2014-11-06 15:51:01 +02:00
static inline int __pm_clk_enable ( struct device * dev , struct pm_clock_entry * ce )
2014-01-14 12:23:42 +00:00
{
2014-11-06 15:51:01 +02:00
int ret ;
if ( ce - > status < PCE_STATUS_ERROR ) {
ret = clk_enable ( ce - > clk ) ;
if ( ! ret )
ce - > status = PCE_STATUS_ENABLED ;
else
dev_err ( dev , " %s: failed to enable clk %p, error %d \n " ,
__func__ , ce - > clk , ret ) ;
}
2014-01-14 12:23:42 +00:00
return ret ;
}
2011-09-26 19:40:23 +02:00
/**
* pm_clk_acquire - Acquire a device clock .
* @ dev : Device whose clock is to be acquired .
* @ ce : PM clock entry corresponding to the clock .
*/
static void pm_clk_acquire ( struct device * dev , struct pm_clock_entry * ce )
{
2014-11-06 15:51:00 +02:00
if ( ! ce - > clk )
ce - > clk = clk_get ( dev , ce - > con_id ) ;
2011-09-26 19:40:23 +02:00
if ( IS_ERR ( ce - > clk ) ) {
ce - > status = PCE_STATUS_ERROR ;
} else {
2014-01-14 12:23:40 +00:00
clk_prepare ( ce - > clk ) ;
2011-09-26 19:40:23 +02:00
ce - > status = PCE_STATUS_ACQUIRED ;
dev_dbg ( dev , " Clock %s managed by runtime PM. \n " , ce - > con_id ) ;
}
}
2014-11-06 15:51:00 +02:00
static int __pm_clk_add ( struct device * dev , const char * con_id ,
struct clk * clk )
2011-04-30 00:25:44 +02:00
{
2011-08-25 15:33:50 +02:00
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
2011-08-25 15:33:50 +02:00
if ( ! psd )
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 ;
}
2014-11-06 15:51:00 +02:00
} else {
if ( IS_ERR ( ce - > clk ) | | ! __clk_get ( clk ) ) {
kfree ( ce ) ;
return - ENOENT ;
}
ce - > clk = clk ;
2011-04-30 00:25:44 +02:00
}
2011-09-26 19:40:23 +02:00
pm_clk_acquire ( dev , ce ) ;
2011-08-25 15:33:50 +02:00
spin_lock_irq ( & psd - > lock ) ;
list_add_tail ( & ce - > node , & psd - > clock_list ) ;
spin_unlock_irq ( & psd - > lock ) ;
2011-04-30 00:25:44 +02:00
return 0 ;
}
2014-11-06 15:51:00 +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 .
* @ con_id : Connection ID of the clock .
*
* Add the clock represented by @ con_id to the list of clocks used for
* the power management of @ dev .
*/
int pm_clk_add ( struct device * dev , const char * con_id )
{
return __pm_clk_add ( dev , con_id , NULL ) ;
}
/**
* pm_clk_add_clk - Start using a device clock for power management .
* @ dev : Device whose clock is going to be used for power management .
* @ clk : Clock pointer
*
* Add the clock to the list of clocks used for the power management of @ dev .
* It will increment refcount on clock pointer , use clk_put ( ) on it when done .
*/
int pm_clk_add_clk ( struct device * dev , struct clk * clk )
{
return __pm_clk_add ( dev , NULL , clk ) ;
}
2011-04-30 00:25:44 +02:00
/**
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
static void __pm_clk_remove ( struct pm_clock_entry * ce )
2011-04-30 00:25:44 +02:00
{
if ( ! ce )
return ;
if ( ce - > status < PCE_STATUS_ERROR ) {
if ( ce - > status = = PCE_STATUS_ENABLED )
2014-01-14 12:23:40 +00:00
clk_disable ( ce - > clk ) ;
2011-04-30 00:25:44 +02:00
2014-01-14 12:23:40 +00:00
if ( ce - > status > = PCE_STATUS_ACQUIRED ) {
clk_unprepare ( ce - > clk ) ;
2011-04-30 00:25:44 +02:00
clk_put ( ce - > clk ) ;
2014-01-14 12:23:40 +00:00
}
2011-04-30 00:25:44 +02:00
}
2011-10-22 00:22:54 +02:00
kfree ( ce - > con_id ) ;
2011-04-30 00:25:44 +02:00
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-08-25 15:33:50 +02:00
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
2011-08-25 15:33:50 +02:00
if ( ! psd )
2011-04-30 00:25:44 +02:00
return ;
2011-08-25 15:33:50 +02:00
spin_lock_irq ( & psd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
list_for_each_entry ( ce , & psd - > clock_list , node ) {
2011-09-26 19:40:23 +02:00
if ( ! con_id & & ! ce - > con_id )
goto remove ;
else if ( ! con_id | | ! ce - > con_id )
2011-04-30 00:25:44 +02:00
continue ;
2011-09-26 19:40:23 +02:00
else if ( ! strcmp ( con_id , ce - > con_id ) )
goto remove ;
2011-04-30 00:25:44 +02:00
}
2011-08-25 15:33:50 +02:00
spin_unlock_irq ( & psd - > lock ) ;
2011-09-26 19:40:23 +02:00
return ;
remove :
list_del ( & ce - > node ) ;
2011-09-26 20:12:45 +02:00
spin_unlock_irq ( & psd - > lock ) ;
2011-09-26 19:40:23 +02:00
__pm_clk_remove ( ce ) ;
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-08-25 15:33:50 +02:00
* Initialize the lock and clock_list members of the device ' s pm_subsys_data
* object .
2011-04-30 00:25:44 +02:00
*/
2011-08-25 15:33:50 +02:00
void pm_clk_init ( struct device * dev )
2011-04-30 00:25:44 +02:00
{
2011-08-25 15:33:50 +02:00
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
2011-08-25 15:34:01 +02:00
if ( psd )
INIT_LIST_HEAD ( & psd - > clock_list ) ;
2011-08-25 15:33:50 +02:00
}
/**
* pm_clk_create - Create and initialize a device ' s list of PM clocks .
* @ dev : Device to create and initialize the list of PM clocks for .
*
* Allocate a struct pm_subsys_data object , initialize its lock and clock_list
* members and make the @ dev ' s power . subsys_data field point to it .
*/
int pm_clk_create ( struct device * dev )
{
2012-08-07 13:50:14 +02:00
return dev_pm_get_subsys_data ( dev ) ;
2011-04-30 00:25:44 +02:00
}
/**
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-08-25 15:33:50 +02:00
* from the struct pm_subsys_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-08-25 15:33:50 +02:00
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce , * c ;
2011-09-26 19:40:23 +02:00
struct list_head list ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
if ( ! psd )
2011-04-30 00:25:44 +02:00
return ;
2011-09-26 19:40:23 +02:00
INIT_LIST_HEAD ( & list ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
spin_lock_irq ( & psd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
list_for_each_entry_safe_reverse ( ce , c , & psd - > clock_list , node )
2011-09-26 19:40:23 +02:00
list_move ( & ce - > node , & list ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
spin_unlock_irq ( & psd - > lock ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:34:01 +02:00
dev_pm_put_subsys_data ( dev ) ;
2011-09-26 19:40:23 +02:00
list_for_each_entry_safe_reverse ( ce , c , & list , node ) {
list_del ( & ce - > node ) ;
__pm_clk_remove ( ce ) ;
}
2011-04-30 00:25:44 +02:00
}
/**
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-08-25 15:33:50 +02:00
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
2011-08-24 21:40:56 +02:00
unsigned long flags ;
2011-04-30 00:25:44 +02:00
dev_dbg ( dev , " %s() \n " , __func__ ) ;
2011-08-25 15:33:50 +02:00
if ( ! psd )
2011-04-30 00:25:44 +02:00
return 0 ;
2011-08-25 15:33:50 +02:00
spin_lock_irqsave ( & psd - > lock , flags ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
list_for_each_entry_reverse ( ce , & psd - > clock_list , node ) {
2011-04-30 00:25:44 +02:00
if ( ce - > status < PCE_STATUS_ERROR ) {
2011-11-10 00:44:10 +01:00
if ( ce - > status = = PCE_STATUS_ENABLED )
clk_disable ( ce - > clk ) ;
2011-04-30 00:25:44 +02:00
ce - > status = PCE_STATUS_ACQUIRED ;
}
}
2011-08-25 15:33:50 +02:00
spin_unlock_irqrestore ( & psd - > lock , flags ) ;
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-08-25 15:33:50 +02:00
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
2011-04-30 00:25:44 +02:00
struct pm_clock_entry * ce ;
2011-08-24 21:40:56 +02:00
unsigned long flags ;
2011-04-30 00:25:44 +02:00
dev_dbg ( dev , " %s() \n " , __func__ ) ;
2011-08-25 15:33:50 +02:00
if ( ! psd )
2011-04-30 00:25:44 +02:00
return 0 ;
2011-08-25 15:33:50 +02:00
spin_lock_irqsave ( & psd - > lock , flags ) ;
2011-04-30 00:25:44 +02:00
2014-11-06 15:51:01 +02:00
list_for_each_entry ( ce , & psd - > clock_list , node )
__pm_clk_enable ( dev , ce ) ;
2011-04-30 00:25:44 +02:00
2011-08-25 15:33:50 +02:00
spin_unlock_irqrestore ( & psd - > lock , flags ) ;
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-08-25 15:33:50 +02:00
error = pm_clk_create ( 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 ;
}
2014-11-27 22:38:05 +01:00
# else /* !CONFIG_PM */
2011-07-01 22:13:37 +02:00
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 ) ) {
2012-10-23 01:18:40 +02:00
clk_prepare_enable ( clk ) ;
2011-04-30 00:25:44 +02:00
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 ) ) {
2012-10-23 01:18:40 +02:00
clk_disable_unprepare ( clk ) ;
2011-04-30 00:25:44 +02:00
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 ;
}
2014-11-27 22:38:05 +01:00
# endif /* !CONFIG_PM */
2011-04-30 00:25:44 +02:00
/**
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 ) ;
}