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>
2017-06-12 18:55:10 +03:00
# include <linux/topology.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
{
2016-07-19 20:52:56 +03:00
/*
* Pass idle state index to arm_cpuidle_suspend which in turn
* will call the CPU ops suspend protocol with idle index as a
* parameter .
*/
return CPU_PM_CPU_IDLE_ENTER ( arm_cpuidle_suspend , idx ) ;
2014-02-28 17:03:44 +04:00
}
2017-06-12 18:55:10 +03:00
static struct cpuidle_driver arm_idle_driver __initdata = {
2015-02-02 18:32:46 +03:00
. 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 ;
2017-06-12 18:55:10 +03:00
struct cpuidle_driver * drv ;
2015-03-05 18:44:42 +03:00
struct cpuidle_device * dev ;
2014-02-28 17:03:44 +04:00
for_each_possible_cpu ( cpu ) {
2017-06-12 18:55:10 +03:00
drv = kmemdup ( & arm_idle_driver , sizeof ( * drv ) , GFP_KERNEL ) ;
if ( ! drv ) {
ret = - ENOMEM ;
goto out_fail ;
}
drv - > cpumask = ( struct cpumask * ) cpumask_of ( cpu ) ;
/*
* 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 .
*/
ret = dt_init_idle_driver ( drv , arm_idle_state_match , 1 ) ;
if ( ret < = 0 ) {
ret = ret ? : - ENODEV ;
2017-08-31 23:24:36 +03:00
goto init_fail ;
2017-06-12 18:55:10 +03:00
}
ret = cpuidle_register_driver ( drv ) ;
if ( ret ) {
pr_err ( " Failed to register cpuidle driver \n " ) ;
2017-08-31 23:24:36 +03:00
goto init_fail ;
2017-06-12 18:55:10 +03:00
}
/*
* Call arch CPU operations in order to initialize
* idle states suspend back - end specific data
*/
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 " ) ;
2016-08-11 16:02:30 +03:00
ret = - ENOMEM ;
2015-03-05 18:44:42 +03:00
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 ;
2017-08-31 23:24:36 +03:00
init_fail :
kfree ( drv ) ;
2015-03-05 18:44:42 +03:00
out_fail :
while ( - - cpu > = 0 ) {
dev = per_cpu ( cpuidle_devices , cpu ) ;
cpuidle_unregister_device ( dev ) ;
kfree ( dev ) ;
2017-06-12 18:55:10 +03:00
drv = cpuidle_get_driver ( ) ;
cpuidle_unregister_driver ( drv ) ;
kfree ( drv ) ;
2015-03-05 18:44:42 +03:00
}
return ret ;
2014-02-28 17:03:44 +04:00
}
2015-02-02 18:32:46 +03:00
device_initcall ( arm_idle_init ) ;