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>
2016-01-07 16:46:13 +01:00
# include <linux/pm_domain.h>
2015-04-23 14:03:09 +05:30
# include <linux/pm_runtime.h>
2011-04-30 00:25:44 +02:00
2015-10-19 10:16:07 +02:00
# ifdef CONFIG_PM_CLK
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
*/
2015-06-29 22:13:38 +01:00
static inline void __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
}
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 ;
2015-05-29 18:35:31 +02:00
dev_dbg ( dev , " Clock %pC con_id %s managed by runtime PM. \n " ,
ce - > clk , ce - > con_id ) ;
2011-09-26 19:40:23 +02:00
}
}
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 ) ;
2015-02-09 10:45:32 +01:00
if ( ! ce )
2011-04-30 00:25:44 +02:00
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 {
2015-10-01 21:05:09 +02:00
if ( IS_ERR ( clk ) ) {
2014-11-06 15:51:00 +02:00
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 ) ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_add ) ;
2014-11-06 15:51:00 +02:00
/**
* 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 .
2015-10-01 21:05:09 +02:00
* The power - management code will take control of the clock reference , so
* callers should not call clk_put ( ) on @ clk after this function sucessfully
* returned .
2014-11-06 15:51:00 +02:00
*/
int pm_clk_add_clk ( struct device * dev , struct clk * clk )
{
return __pm_clk_add ( dev , NULL , clk ) ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_add_clk ) ;
2014-11-06 15:51:00 +02:00
2016-03-15 11:33:40 +00:00
2016-06-21 11:33:25 +01:00
/**
* of_pm_clk_add_clk - Start using a device clock for power management .
* @ dev : Device whose clock is going to be used for power management .
* @ name : Name of clock that is going to be used for power management .
*
* Add the clock described in the ' clocks ' device - tree node that matches
* with the ' name ' provided , to the list of clocks used for the power
* management of @ dev . On success , returns 0. Returns a negative error
* code if the clock is not found or cannot be added .
*/
int of_pm_clk_add_clk ( struct device * dev , const char * name )
{
struct clk * clk ;
int ret ;
if ( ! dev | | ! dev - > of_node | | ! name )
return - EINVAL ;
clk = of_clk_get_by_name ( dev - > of_node , name ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
ret = pm_clk_add_clk ( dev , clk ) ;
if ( ret ) {
clk_put ( clk ) ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( of_pm_clk_add_clk ) ;
2016-03-15 11:33:40 +00:00
/**
* of_pm_clk_add_clks - Start using device clock ( s ) for power management .
* @ dev : Device whose clock ( s ) is going to be used for power management .
*
* Add a series of clocks described in the ' clocks ' device - tree node for
* a device to the list of clocks used for the power management of @ dev .
* On success , returns the number of clocks added . Returns a negative
* error code if there are no clocks in the device node for the device
* or if adding a clock fails .
*/
int of_pm_clk_add_clks ( struct device * dev )
{
struct clk * * clks ;
unsigned int i , count ;
int ret ;
if ( ! dev | | ! dev - > of_node )
return - EINVAL ;
count = of_count_phandle_with_args ( dev - > of_node , " clocks " ,
" #clock-cells " ) ;
2016-04-16 13:50:03 +01:00
if ( count < = 0 )
2016-03-15 11:33:40 +00:00
return - ENODEV ;
clks = kcalloc ( count , sizeof ( * clks ) , GFP_KERNEL ) ;
if ( ! clks )
return - ENOMEM ;
for ( i = 0 ; i < count ; i + + ) {
clks [ i ] = of_clk_get ( dev - > of_node , i ) ;
if ( IS_ERR ( clks [ i ] ) ) {
ret = PTR_ERR ( clks [ i ] ) ;
goto error ;
}
ret = pm_clk_add_clk ( dev , clks [ i ] ) ;
if ( ret ) {
clk_put ( clks [ i ] ) ;
goto error ;
}
}
kfree ( clks ) ;
return i ;
error :
while ( i - - )
pm_clk_remove_clk ( dev , clks [ i ] ) ;
kfree ( clks ) ;
return ret ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( of_pm_clk_add_clks ) ;
2016-03-15 11:33:40 +00:00
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
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_remove ) ;
2011-04-30 00:25:44 +02:00
2016-03-15 11:33:40 +00:00
/**
* pm_clk_remove_clk - Stop using a device clock for power management .
* @ dev : Device whose clock should not be used for PM any more .
* @ clk : Clock pointer
*
* Remove the clock pointed to by @ clk from the list of clocks used for
* the power management of @ dev .
*/
void pm_clk_remove_clk ( struct device * dev , struct clk * clk )
{
struct pm_subsys_data * psd = dev_to_psd ( dev ) ;
struct pm_clock_entry * ce ;
if ( ! psd | | ! clk )
return ;
spin_lock_irq ( & psd - > lock ) ;
list_for_each_entry ( ce , & psd - > clock_list , node ) {
if ( clk = = ce - > clk )
goto remove ;
}
spin_unlock_irq ( & psd - > lock ) ;
return ;
remove :
list_del ( & ce - > node ) ;
spin_unlock_irq ( & psd - > lock ) ;
__pm_clk_remove ( ce ) ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_remove_clk ) ;
2016-03-15 11:33:40 +00:00
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
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_init ) ;
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
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_create ) ;
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
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_destroy ) ;
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 ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_suspend ) ;
2011-04-30 00:25:44 +02:00
/**
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 ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_resume ) ;
2011-04-30 00:25:44 +02:00
/**
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 ;
2016-01-07 16:46:13 +01:00
dev_pm_domain_set ( dev , 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 ;
2016-01-07 16:46:13 +01:00
dev_pm_domain_set ( dev , NULL ) ;
2011-07-01 22:13:44 +02:00
pm_clk_destroy ( dev ) ;
2011-04-30 00:25:44 +02:00
break ;
}
return 0 ;
}
2015-04-23 14:03:09 +05:30
int pm_clk_runtime_suspend ( struct device * dev )
{
int ret ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
ret = pm_generic_runtime_suspend ( dev ) ;
if ( ret ) {
dev_err ( dev , " failed to suspend device \n " ) ;
return ret ;
}
ret = pm_clk_suspend ( dev ) ;
if ( ret ) {
dev_err ( dev , " failed to suspend clock \n " ) ;
pm_generic_runtime_resume ( dev ) ;
return ret ;
}
return 0 ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_runtime_suspend ) ;
2015-04-23 14:03:09 +05:30
int pm_clk_runtime_resume ( struct device * dev )
{
int ret ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
ret = pm_clk_resume ( dev ) ;
if ( ret ) {
dev_err ( dev , " failed to resume clock \n " ) ;
return ret ;
}
return pm_generic_runtime_resume ( dev ) ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_runtime_resume ) ;
2015-04-23 14:03:09 +05:30
2015-10-19 10:16:07 +02:00
# else /* !CONFIG_PM_CLK */
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 ;
2016-01-07 12:49:31 +02:00
case BUS_NOTIFY_DRIVER_NOT_BOUND :
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 ;
}
2015-10-19 10:16:07 +02:00
# endif /* !CONFIG_PM_CLK */
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 ) ;
}
2016-06-05 13:58:15 -04:00
EXPORT_SYMBOL_GPL ( pm_clk_add_notifier ) ;