@ -20,6 +20,10 @@
# include <asm/opal.h>
# include <asm/runlatch.h>
/*
* Expose only those Hardware idle states via the cpuidle framework
* that have latency value below POWERNV_THRESHOLD_LATENCY_NS .
*/
# define POWERNV_THRESHOLD_LATENCY_NS 200000
static struct cpuidle_driver powernv_idle_driver = {
@ -167,6 +171,24 @@ static int powernv_cpuidle_driver_init(void)
return 0 ;
}
static inline void add_powernv_state ( int index , const char * name ,
unsigned int flags ,
int ( * idle_fn ) ( struct cpuidle_device * ,
struct cpuidle_driver * ,
int ) ,
unsigned int target_residency ,
unsigned int exit_latency ,
u64 psscr_val )
{
strlcpy ( powernv_states [ index ] . name , name , CPUIDLE_NAME_LEN ) ;
strlcpy ( powernv_states [ index ] . desc , name , CPUIDLE_NAME_LEN ) ;
powernv_states [ index ] . flags = flags ;
powernv_states [ index ] . target_residency = target_residency ;
powernv_states [ index ] . exit_latency = exit_latency ;
powernv_states [ index ] . enter = idle_fn ;
stop_psscr_table [ index ] = psscr_val ;
}
static int powernv_add_idle_states ( void )
{
struct device_node * power_mgt ;
@ -236,6 +258,7 @@ static int powernv_add_idle_states(void)
" ibm,cpu-idle-state-residency-ns " , residency_ns , dt_idle_states ) ;
for ( i = 0 ; i < dt_idle_states ; i + + ) {
unsigned int exit_latency , target_residency ;
/*
* If an idle state has exit latency beyond
* POWERNV_THRESHOLD_LATENCY_NS then don ' t use it
@ -243,28 +266,33 @@ static int powernv_add_idle_states(void)
*/
if ( latency_ns [ i ] > POWERNV_THRESHOLD_LATENCY_NS )
continue ;
/*
* Firmware passes residency and latency values in ns .
* cpuidle expects it in us .
*/
exit_latency = latency_ns [ i ] / 1000 ;
if ( ! rc )
target_residency = residency_ns [ i ] / 1000 ;
else
target_residency = 0 ;
/*
* Cpuidle accepts exit_latency and target_residency in us .
* Use default target_residency values if f / w does not expose it .
* For nap and fastsleep , use default target_residency
* values if f / w does not expose it .
*/
if ( flags [ i ] & OPAL_PM_NAP_ENABLED ) {
if ( ! rc )
target_residency = 100 ;
/* Add NAP state */
strcpy ( powernv_states [ nr_idle_states ] . name , " Nap " ) ;
strcpy ( powernv_states [ nr_idle_states ] . desc , " Nap " ) ;
powernv_states [ nr_idle_states ] . flags = 0 ;
powernv_states [ nr_idle_states ] . target_residency = 100 ;
powernv_states [ nr_idle_states ] . enter = nap_loop ;
add_powernv_state ( nr_idle_states , " Nap " ,
CPUIDLE_FLAG_NONE , nap_loop ,
target_residency , exit_latency , 0 ) ;
} else if ( ( flags [ i ] & OPAL_PM_STOP_INST_FAST ) & &
! ( flags [ i ] & OPAL_PM_TIMEBASE_STOP ) ) {
strncpy ( powernv_states [ nr_idle_states ] . name ,
names [ i ] , CPUIDLE_NAME_LEN ) ;
strncpy ( powernv_states [ nr_idle_states ] . desc ,
names [ i ] , CPUIDLE_NAME_LEN ) ;
powernv_states [ nr_idle_states ] . flags = 0 ;
powernv_states [ nr_idle_states ] . enter = stop_loop ;
stop_psscr_table [ nr_idle_states ] = psscr_val [ i ] ;
add_powernv_state ( nr_idle_states , names [ i ] ,
CPUIDLE_FLAG_NONE , stop_loop ,
target_residency , exit_latency ,
psscr_val [ i ] ) ;
}
/*
@ -274,32 +302,21 @@ static int powernv_add_idle_states(void)
# ifdef CONFIG_TICK_ONESHOT
if ( flags [ i ] & OPAL_PM_SLEEP_ENABLED | |
flags [ i ] & OPAL_PM_SLEEP_ENABLED_ER1 ) {
if ( ! rc )
target_residency = 300000 ;
/* Add FASTSLEEP state */
strcpy ( powernv_states [ nr_idle_states ] . name , " FastSleep " ) ;
strcpy ( powernv_states [ nr_idle_states ] . desc , " FastSleep " ) ;
powernv_states [ nr_idle_states ] . flags = CPUIDLE_FLAG_TIMER_STOP ;
powernv_states [ nr_idle_states ] . target_residency = 300000 ;
powernv_states [ nr_idle_states ] . enter = fastsleep_loop ;
add_powernv_state ( nr_idle_states , " FastSleep " ,
CPUIDLE_FLAG_TIMER_STOP ,
fastsleep_loop ,
target_residency , exit_latency , 0 ) ;
} else if ( ( flags [ i ] & OPAL_PM_STOP_INST_DEEP ) & &
( flags [ i ] & OPAL_PM_TIMEBASE_STOP ) ) {
strncpy ( powernv_states [ nr_idle_states ] . name ,
names [ i ] , CPUIDLE_NAME_LEN ) ;
strncpy ( powernv_states [ nr_idle_states ] . desc ,
names [ i ] , CPUIDLE_NAME_LEN ) ;
powernv_states [ nr_idle_states ] . flags = CPUIDLE_FLAG_TIMER_STOP ;
powernv_states [ nr_idle_states ] . enter = stop_loop ;
stop_psscr_table [ nr_idle_states ] = psscr_val [ i ] ;
add_powernv_state ( nr_idle_states , names [ i ] ,
CPUIDLE_FLAG_TIMER_STOP , stop_loop ,
target_residency , exit_latency ,
psscr_val [ i ] ) ;
}
# endif
powernv_states [ nr_idle_states ] . exit_latency =
( ( unsigned int ) latency_ns [ i ] ) / 1000 ;
if ( ! rc ) {
powernv_states [ nr_idle_states ] . target_residency =
( ( unsigned int ) residency_ns [ i ] ) / 1000 ;
}
nr_idle_states + + ;
}
out :