2014-02-28 17:03:44 +04:00
/*
2015-02-02 18:32:46 +03:00
* ARM / ARM64 generic CPU idle driver .
2014-02-28 17:03:44 +04:00
*
* Copyright ( C ) 2014 ARM Ltd .
* Author : Lorenzo Pieralisi < lorenzo . pieralisi @ arm . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2015-02-02 18:32:46 +03:00
# define pr_fmt(fmt) "CPUidle arm: " fmt
2014-02-28 17:03:44 +04:00
# include <linux/cpuidle.h>
# include <linux/cpumask.h>
# include <linux/cpu_pm.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
2015-03-05 18:44:42 +03:00
# include <linux/slab.h>
2014-02-28 17:03:44 +04:00
# include <asm/cpuidle.h>
# include "dt_idle_states.h"
/*
2015-02-02 18:32:46 +03:00
* arm_enter_idle_state - Programs CPU to enter the specified state
2014-02-28 17:03:44 +04:00
*
* dev : cpuidle device
* drv : cpuidle driver
* idx : state index
*
* Called from the CPUidle framework to program the device to the
* specified target state selected by the governor .
*/
2015-02-02 18:32:46 +03:00
static int arm_enter_idle_state ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv , int idx )
2014-02-28 17:03:44 +04:00
{
int ret ;
if ( ! idx ) {
cpu_do_idle ( ) ;
return idx ;
}
ret = cpu_pm_enter ( ) ;
if ( ! ret ) {
/*
* Pass idle state index to cpu_suspend which in turn will
* call the CPU ops suspend protocol with idle index as a
* parameter .
*/
2016-04-26 14:15:01 +03:00
ret = arm_cpuidle_suspend ( idx ) ;
2014-02-28 17:03:44 +04:00
cpu_pm_exit ( ) ;
}
return ret ? - 1 : idx ;
}
2015-02-02 18:32:46 +03:00
static struct cpuidle_driver arm_idle_driver = {
. name = " arm_idle " ,
2014-02-28 17:03:44 +04:00
. owner = THIS_MODULE ,
/*
* State at index 0 is standby wfi and considered standard
* on all ARM platforms . If in some platforms simple wfi
* can ' t be used as " state 0 " , DT bindings must be implemented
* to work around this issue and allow installing a special
* handler for idle state index 0.
*/
. states [ 0 ] = {
2015-02-02 18:32:46 +03:00
. enter = arm_enter_idle_state ,
2014-02-28 17:03:44 +04:00
. exit_latency = 1 ,
. target_residency = 1 ,
. power_usage = UINT_MAX ,
. name = " WFI " ,
2015-02-02 18:32:46 +03:00
. desc = " ARM WFI " ,
2014-02-28 17:03:44 +04:00
}
} ;
2015-02-02 18:32:46 +03:00
static const struct of_device_id arm_idle_state_match [ ] __initconst = {
2014-02-28 17:03:44 +04:00
{ . compatible = " arm,idle-state " ,
2015-02-02 18:32:46 +03:00
. data = arm_enter_idle_state } ,
2014-02-28 17:03:44 +04:00
{ } ,
} ;
/*
2015-02-02 18:32:46 +03:00
* arm_idle_init
2014-02-28 17:03:44 +04:00
*
2015-02-02 18:32:46 +03:00
* Registers the arm specific cpuidle driver with the cpuidle
2014-02-28 17:03:44 +04:00
* framework . It relies on core code to parse the idle states
* and initialize them using driver data structures accordingly .
*/
2015-02-02 18:32:46 +03:00
static int __init arm_idle_init ( void )
2014-02-28 17:03:44 +04:00
{
int cpu , ret ;
2015-02-02 18:32:46 +03:00
struct cpuidle_driver * drv = & arm_idle_driver ;
2015-03-05 18:44:42 +03:00
struct cpuidle_device * dev ;
2014-02-28 17:03:44 +04:00
/*
* Initialize idle states data , starting at index 1.
* This driver is DT only , if no DT idle states are detected ( ret = = 0 )
* let the driver initialization fail accordingly since there is no
* reason to initialize the idle driver if only wfi is supported .
*/
2015-02-02 18:32:46 +03:00
ret = dt_init_idle_driver ( drv , arm_idle_state_match , 1 ) ;
2014-10-15 21:29:54 +04:00
if ( ret < = 0 )
2014-02-28 17:03:44 +04:00
return ret ? : - ENODEV ;
2015-03-05 18:44:42 +03:00
ret = cpuidle_register_driver ( drv ) ;
if ( ret ) {
pr_err ( " Failed to register cpuidle driver \n " ) ;
return ret ;
}
2014-02-28 17:03:44 +04:00
/*
* Call arch CPU operations in order to initialize
* idle states suspend back - end specific data
*/
for_each_possible_cpu ( cpu ) {
2015-02-02 18:32:46 +03:00
ret = arm_cpuidle_init ( cpu ) ;
2015-03-05 18:44:42 +03:00
/*
* Skip the cpuidle device initialization if the reported
* failure is a HW misconfiguration / breakage ( - ENXIO ) .
*/
if ( ret = = - ENXIO )
continue ;
2014-02-28 17:03:44 +04:00
if ( ret ) {
pr_err ( " CPU %d failed to init idle CPU ops \n " , cpu ) ;
2015-03-05 18:44:42 +03:00
goto out_fail ;
}
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
pr_err ( " Failed to allocate cpuidle device \n " ) ;
goto out_fail ;
}
dev - > cpu = cpu ;
ret = cpuidle_register_device ( dev ) ;
if ( ret ) {
pr_err ( " Failed to register cpuidle device for CPU %d \n " ,
cpu ) ;
kfree ( dev ) ;
goto out_fail ;
2014-02-28 17:03:44 +04:00
}
}
2015-03-05 18:44:42 +03:00
return 0 ;
out_fail :
while ( - - cpu > = 0 ) {
dev = per_cpu ( cpuidle_devices , cpu ) ;
cpuidle_unregister_device ( dev ) ;
kfree ( dev ) ;
}
cpuidle_unregister_driver ( drv ) ;
return ret ;
2014-02-28 17:03:44 +04:00
}
2015-02-02 18:32:46 +03:00
device_initcall ( arm_idle_init ) ;