2012-03-20 15:22:42 -05:00
/*
* Copyright 2012 Linaro Ltd .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/cpuidle.h>
2015-02-02 16:32:45 +01:00
# include <linux/of.h>
# include <linux/of_device.h>
2015-02-02 16:32:46 +01:00
# include <asm/cpuidle.h>
2012-03-20 15:22:42 -05:00
2015-02-02 16:32:45 +01:00
extern struct of_cpuidle_method __cpuidle_method_of_table [ ] ;
static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel
__used __section ( __cpuidle_method_of_table_end ) ;
2016-08-10 22:46:49 +01:00
static struct cpuidle_ops cpuidle_ops [ NR_CPUS ] __ro_after_init ;
2015-02-02 16:32:45 +01:00
2015-03-24 10:49:55 +01:00
/**
* arm_cpuidle_simple_enter ( ) - a wrapper to cpu_do_idle ( )
* @ dev : not used
* @ drv : not used
* @ index : not used
*
* A trivial wrapper to allow the cpu_do_idle function to be assigned as a
* cpuidle callback by matching the function signature .
*
* Returns the index passed as parameter
*/
2012-03-20 15:22:42 -05:00
int arm_cpuidle_simple_enter ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv , int index )
{
cpu_do_idle ( ) ;
return index ;
}
2015-02-02 16:32:45 +01:00
2015-03-24 10:49:55 +01:00
/**
* arm_cpuidle_suspend ( ) - function to enter low power idle states
* @ index : an integer used as an identifier for the low level PM callbacks
*
* This function calls the underlying arch specific low level PM code as
* registered at the init time .
*
2016-07-14 11:39:18 +01:00
* Returns the result of the suspend callback .
2015-03-24 10:49:55 +01:00
*/
2015-02-02 16:32:45 +01:00
int arm_cpuidle_suspend ( int index )
{
int cpu = smp_processor_id ( ) ;
2016-07-14 11:39:18 +01:00
return cpuidle_ops [ cpu ] . suspend ( index ) ;
2015-02-02 16:32:45 +01:00
}
2015-03-24 10:49:55 +01:00
/**
* arm_cpuidle_get_ops ( ) - find a registered cpuidle_ops by name
* @ method : the method name
*
* Search in the __cpuidle_method_of_table array the cpuidle ops matching the
* method name .
*
* Returns a struct cpuidle_ops pointer , NULL if not found .
*/
2016-03-22 22:42:41 +08:00
static const struct cpuidle_ops * __init arm_cpuidle_get_ops ( const char * method )
2015-02-02 16:32:45 +01:00
{
struct of_cpuidle_method * m = __cpuidle_method_of_table ;
for ( ; m - > method ; m + + )
if ( ! strcmp ( m - > method , method ) )
return m - > ops ;
return NULL ;
}
2015-03-24 10:49:55 +01:00
/**
* arm_cpuidle_read_ops ( ) - Initialize the cpuidle ops with the device tree
* @ dn : a pointer to a struct device node corresponding to a cpu node
* @ cpu : the cpu identifier
*
* Get the method name defined in the ' enable - method ' property , retrieve the
* associated cpuidle_ops and do a struct copy . This copy is needed because all
2016-03-22 22:42:41 +08:00
* cpuidle_ops are tagged __initconst and will be unloaded after the init
2015-03-24 10:49:55 +01:00
* process .
*
* Return 0 on sucess , - ENOENT if no ' enable - method ' is defined , - EOPNOTSUPP if
2016-07-14 11:39:18 +01:00
* no cpuidle_ops is registered for the ' enable - method ' , or if either init or
* suspend callback isn ' t defined .
2015-03-24 10:49:55 +01:00
*/
2015-02-02 16:32:45 +01:00
static int __init arm_cpuidle_read_ops ( struct device_node * dn , int cpu )
{
const char * enable_method ;
2016-03-22 22:42:41 +08:00
const struct cpuidle_ops * ops ;
2015-02-02 16:32:45 +01:00
enable_method = of_get_property ( dn , " enable-method " , NULL ) ;
if ( ! enable_method )
return - ENOENT ;
ops = arm_cpuidle_get_ops ( enable_method ) ;
if ( ! ops ) {
pr_warn ( " %s: unsupported enable-method property: %s \n " ,
dn - > full_name , enable_method ) ;
return - EOPNOTSUPP ;
}
2016-07-14 11:39:18 +01:00
if ( ! ops - > init | | ! ops - > suspend ) {
pr_warn ( " cpuidle_ops '%s': no init or suspend callback \n " ,
2016-07-14 11:37:27 +01:00
enable_method ) ;
return - EOPNOTSUPP ;
}
2015-02-02 16:32:45 +01:00
cpuidle_ops [ cpu ] = * ops ; /* structure copy */
pr_notice ( " cpuidle: enable-method property '%s' "
" found operations \n " , enable_method ) ;
return 0 ;
}
2015-03-24 10:49:55 +01:00
/**
* arm_cpuidle_init ( ) - Initialize cpuidle_ops for a specific cpu
* @ cpu : the cpu to be initialized
*
* Initialize the cpuidle ops with the device for the cpu and then call
* the cpu ' s idle initialization callback . This may fail if the underlying HW
* is not operational .
*
* Returns :
* 0 on success ,
* - ENODEV if it fails to find the cpu node in the device tree ,
2016-07-14 11:37:27 +01:00
* - EOPNOTSUPP if it does not find a registered and valid cpuidle_ops for
* this cpu ,
2015-03-24 10:49:55 +01:00
* - ENOENT if it fails to find an ' enable - method ' property ,
* - ENXIO if the HW reports a failure or a misconfiguration ,
* - ENOMEM if the HW report an memory allocation failure
*/
2015-02-02 16:32:45 +01:00
int __init arm_cpuidle_init ( int cpu )
{
struct device_node * cpu_node = of_cpu_device_node_get ( cpu ) ;
int ret ;
if ( ! cpu_node )
return - ENODEV ;
ret = arm_cpuidle_read_ops ( cpu_node , cpu ) ;
2016-07-14 11:37:27 +01:00
if ( ! ret )
2015-02-02 16:32:45 +01:00
ret = cpuidle_ops [ cpu ] . init ( cpu_node , cpu ) ;
of_node_put ( cpu_node ) ;
return ret ;
}