2014-02-28 17:03:44 +04:00
/*
* ARM64 generic CPU idle driver .
*
* 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 .
*/
# define pr_fmt(fmt) "CPUidle arm64: " fmt
# 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>
# include <asm/cpuidle.h>
# include "dt_idle_states.h"
/*
* arm64_enter_idle_state - Programs CPU to enter the specified state
*
* 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 .
*/
static int arm64_enter_idle_state ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv , int idx )
{
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 .
*/
2015-02-02 18:32:45 +03:00
arm_cpuidle_suspend ( idx ) ;
2014-02-28 17:03:44 +04:00
cpu_pm_exit ( ) ;
}
return ret ? - 1 : idx ;
}
static struct cpuidle_driver arm64_idle_driver = {
. name = " arm64_idle " ,
. 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 ] = {
. enter = arm64_enter_idle_state ,
. exit_latency = 1 ,
. target_residency = 1 ,
. power_usage = UINT_MAX ,
. name = " WFI " ,
. desc = " ARM64 WFI " ,
}
} ;
static const struct of_device_id arm64_idle_state_match [ ] __initconst = {
{ . compatible = " arm,idle-state " ,
. data = arm64_enter_idle_state } ,
{ } ,
} ;
/*
* arm64_idle_init
*
* Registers the arm64 specific cpuidle driver with the cpuidle
* framework . It relies on core code to parse the idle states
* and initialize them using driver data structures accordingly .
*/
static int __init arm64_idle_init ( void )
{
int cpu , ret ;
struct cpuidle_driver * drv = & arm64_idle_driver ;
/*
* 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 , arm64_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 ;
/*
* 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 ) ;
2014-02-28 17:03:44 +04:00
if ( ret ) {
pr_err ( " CPU %d failed to init idle CPU ops \n " , cpu ) ;
return ret ;
}
}
2014-10-15 21:29:54 +04:00
return cpuidle_register ( drv , NULL ) ;
2014-02-28 17:03:44 +04:00
}
device_initcall ( arm64_idle_init ) ;