2005-04-17 02:20:36 +04:00
/*
* drivers / cpufreq / cpufreq_stats . c
*
* Copyright ( C ) 2003 - 2004 Venkatesh Pallipadi < venkatesh . pallipadi @ intel . com > .
2009-01-18 09:49:04 +03:00
* ( C ) 2004 Zou Nan hai < nanhai . zou @ intel . com > .
2005-04-17 02:20:36 +04:00
*
* 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 .
*/
# include <linux/cpu.h>
# include <linux/cpufreq.h>
2011-05-27 21:23:32 +04:00
# include <linux/module.h>
2013-08-06 21:23:03 +04:00
# include <linux/slab.h>
2014-03-05 19:33:42 +04:00
# include <linux/cputime.h>
2005-04-17 02:20:36 +04:00
2016-05-31 23:14:44 +03:00
static DEFINE_SPINLOCK ( cpufreq_stats_lock ) ;
2005-04-17 02:20:36 +04:00
struct cpufreq_stats {
unsigned int total_trans ;
2013-06-19 12:49:33 +04:00
unsigned long long last_time ;
2005-04-17 02:20:36 +04:00
unsigned int max_state ;
unsigned int state_num ;
unsigned int last_index ;
2012-10-26 02:51:21 +04:00
u64 * time_in_state ;
2005-04-17 02:20:36 +04:00
unsigned int * freq_table ;
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
unsigned int * trans_table ;
# endif
} ;
2015-01-06 18:39:07 +03:00
static int cpufreq_stats_update ( struct cpufreq_stats * stats )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:03 +03:00
unsigned long long cur_time = get_jiffies_64 ( ) ;
2005-05-26 01:46:50 +04:00
2005-04-17 02:20:36 +04:00
spin_lock ( & cpufreq_stats_lock ) ;
2015-01-06 18:39:12 +03:00
stats - > time_in_state [ stats - > last_index ] + = cur_time - stats - > last_time ;
2015-01-06 18:39:07 +03:00
stats - > last_time = cur_time ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & cpufreq_stats_lock ) ;
return 0 ;
}
2009-01-18 09:49:04 +03:00
static ssize_t show_total_trans ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
2015-01-13 09:04:00 +03:00
return sprintf ( buf , " %d \n " , policy - > stats - > total_trans ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-18 09:49:04 +03:00
static ssize_t show_time_in_state ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2005-04-17 02:20:36 +04:00
ssize_t len = 0 ;
int i ;
2015-01-13 09:04:00 +03:00
2016-05-31 23:14:44 +03:00
if ( policy - > fast_switch_enabled )
return 0 ;
2015-01-06 18:39:07 +03:00
cpufreq_stats_update ( stats ) ;
for ( i = 0 ; i < stats - > state_num ; i + + ) {
len + = sprintf ( buf + len , " %u %llu \n " , stats - > freq_table [ i ] ,
2009-01-18 09:49:04 +03:00
( unsigned long long )
2015-01-06 18:39:07 +03:00
jiffies_64_to_clock_t ( stats - > time_in_state [ i ] ) ) ;
2005-04-17 02:20:36 +04:00
}
return len ;
}
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2009-01-18 09:49:04 +03:00
static ssize_t show_trans_table ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2005-04-17 02:20:36 +04:00
ssize_t len = 0 ;
int i , j ;
2016-05-31 23:14:44 +03:00
if ( policy - > fast_switch_enabled )
return 0 ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " From : To \n " ) ;
len + = snprintf ( buf + len , PAGE_SIZE - len , " : " ) ;
2015-01-06 18:39:07 +03:00
for ( i = 0 ; i < stats - > state_num ; i + + ) {
2005-05-26 01:46:50 +04:00
if ( len > = PAGE_SIZE )
break ;
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u " ,
2015-01-06 18:39:07 +03:00
stats - > freq_table [ i ] ) ;
2005-05-26 01:46:50 +04:00
}
if ( len > = PAGE_SIZE )
2008-02-16 13:41:25 +03:00
return PAGE_SIZE ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " \n " ) ;
2015-01-06 18:39:07 +03:00
for ( i = 0 ; i < stats - > state_num ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( len > = PAGE_SIZE )
break ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u: " ,
2015-01-06 18:39:07 +03:00
stats - > freq_table [ i ] ) ;
2005-04-17 02:20:36 +04:00
2015-01-06 18:39:07 +03:00
for ( j = 0 ; j < stats - > state_num ; j + + ) {
2005-04-17 02:20:36 +04:00
if ( len > = PAGE_SIZE )
break ;
2005-05-26 01:46:50 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " %9u " ,
2015-01-06 18:39:07 +03:00
stats - > trans_table [ i * stats - > max_state + j ] ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-16 13:41:25 +03:00
if ( len > = PAGE_SIZE )
break ;
2005-04-17 02:20:36 +04:00
len + = snprintf ( buf + len , PAGE_SIZE - len , " \n " ) ;
}
2008-02-16 13:41:25 +03:00
if ( len > = PAGE_SIZE )
return PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
return len ;
}
2013-02-04 15:38:52 +04:00
cpufreq_freq_attr_ro ( trans_table ) ;
2005-04-17 02:20:36 +04:00
# endif
2013-02-04 15:38:52 +04:00
cpufreq_freq_attr_ro ( total_trans ) ;
cpufreq_freq_attr_ro ( time_in_state ) ;
2005-04-17 02:20:36 +04:00
static struct attribute * default_attrs [ ] = {
2013-02-04 15:38:52 +04:00
& total_trans . attr ,
& time_in_state . attr ,
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2013-02-04 15:38:52 +04:00
& trans_table . attr ,
2005-04-17 02:20:36 +04:00
# endif
NULL
} ;
static struct attribute_group stats_attr_group = {
. attrs = default_attrs ,
. name = " stats "
} ;
2015-01-06 18:39:07 +03:00
static int freq_table_get_index ( struct cpufreq_stats * stats , unsigned int freq )
2005-04-17 02:20:36 +04:00
{
int index ;
2015-01-06 18:39:07 +03:00
for ( index = 0 ; index < stats - > max_state ; index + + )
if ( stats - > freq_table [ index ] = = freq )
2005-04-17 02:20:36 +04:00
return index ;
return - 1 ;
}
2016-05-31 23:14:44 +03:00
void cpufreq_stats_free_table ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2013-01-14 17:23:03 +04:00
2015-01-13 09:04:00 +03:00
/* Already freed */
2015-01-06 18:39:07 +03:00
if ( ! stats )
2014-01-07 05:40:12 +04:00
return ;
2015-01-06 18:39:07 +03:00
pr_debug ( " %s: Free stats table \n " , __func__ ) ;
2014-01-07 05:40:12 +04:00
sysfs_remove_group ( & policy - > kobj , & stats_attr_group ) ;
2015-01-06 18:39:07 +03:00
kfree ( stats - > time_in_state ) ;
kfree ( stats ) ;
2015-01-13 09:04:00 +03:00
policy - > stats = NULL ;
2011-05-02 22:29:17 +04:00
}
2016-05-31 23:14:44 +03:00
void cpufreq_stats_create_table ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2015-01-06 18:39:11 +03:00
unsigned int i = 0 , count = 0 , ret = - ENOMEM ;
2015-01-06 18:39:07 +03:00
struct cpufreq_stats * stats ;
2005-04-17 02:20:36 +04:00
unsigned int alloc_size ;
unsigned int cpu = policy - > cpu ;
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos , * table ;
2014-02-28 05:58:36 +04:00
2015-01-06 18:39:11 +03:00
/* We need cpufreq table for creating stats table */
2014-02-28 05:58:36 +04:00
table = cpufreq_frequency_get_table ( cpu ) ;
if ( unlikely ( ! table ) )
2016-05-31 23:14:44 +03:00
return ;
2014-02-28 05:58:36 +04:00
2015-01-06 18:39:01 +03:00
/* stats already initialized */
2015-01-13 09:04:00 +03:00
if ( policy - > stats )
2016-05-31 23:14:44 +03:00
return ;
2015-01-06 18:39:01 +03:00
2015-01-06 18:39:07 +03:00
stats = kzalloc ( sizeof ( * stats ) , GFP_KERNEL ) ;
2015-01-06 18:39:11 +03:00
if ( ! stats )
2016-05-31 23:14:44 +03:00
return ;
2005-04-17 02:20:36 +04:00
2015-01-06 18:39:11 +03:00
/* Find total allocation size */
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table )
2005-04-17 02:20:36 +04:00
count + + ;
2012-10-26 02:51:21 +04:00
alloc_size = count * sizeof ( int ) + count * sizeof ( u64 ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
alloc_size + = count * count * sizeof ( int ) ;
# endif
2015-01-06 18:39:11 +03:00
/* Allocate memory for time_in_state/freq_table/trans_table in one go */
2015-01-06 18:39:07 +03:00
stats - > time_in_state = kzalloc ( alloc_size , GFP_KERNEL ) ;
2015-01-06 18:39:11 +03:00
if ( ! stats - > time_in_state )
goto free_stat ;
2015-01-06 18:39:07 +03:00
stats - > freq_table = ( unsigned int * ) ( stats - > time_in_state + count ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2015-01-06 18:39:07 +03:00
stats - > trans_table = stats - > freq_table + count ;
2005-04-17 02:20:36 +04:00
# endif
2015-01-06 18:39:11 +03:00
stats - > max_state = count ;
/* Find valid-unique entries */
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table )
2015-01-06 18:39:07 +03:00
if ( freq_table_get_index ( stats , pos - > frequency ) = = - 1 )
stats - > freq_table [ i + + ] = pos - > frequency ;
2015-01-06 18:39:11 +03:00
2015-01-06 18:39:15 +03:00
stats - > state_num = i ;
2015-01-06 18:39:07 +03:00
stats - > last_time = get_jiffies_64 ( ) ;
stats - > last_index = freq_table_get_index ( stats , policy - > cur ) ;
2015-01-06 18:39:11 +03:00
policy - > stats = stats ;
ret = sysfs_create_group ( & policy - > kobj , & stats_attr_group ) ;
if ( ! ret )
2016-05-31 23:14:44 +03:00
return ;
2015-01-06 18:39:11 +03:00
/* We failed, release resources */
2015-01-13 09:04:00 +03:00
policy - > stats = NULL ;
2015-01-06 18:39:11 +03:00
kfree ( stats - > time_in_state ) ;
free_stat :
kfree ( stats ) ;
2014-01-07 05:40:13 +04:00
}
2016-05-31 23:14:44 +03:00
void cpufreq_stats_record_transition ( struct cpufreq_policy * policy ,
unsigned int new_freq )
2005-04-17 02:20:36 +04:00
{
2016-05-31 23:14:44 +03:00
struct cpufreq_stats * stats = policy - > stats ;
2005-04-17 02:20:36 +04:00
int old_index , new_index ;
2016-05-31 23:14:44 +03:00
if ( ! stats ) {
2015-01-13 09:04:00 +03:00
pr_debug ( " %s: No stats found \n " , __func__ ) ;
2016-05-31 23:14:44 +03:00
return ;
2015-01-13 09:04:00 +03:00
}
2015-01-06 18:39:07 +03:00
old_index = stats - > last_index ;
2016-05-31 23:14:44 +03:00
new_index = freq_table_get_index ( stats , new_freq ) ;
2005-04-17 02:20:36 +04:00
2015-01-06 18:39:07 +03:00
/* We can't do stats->time_in_state[-1]= .. */
2016-05-31 23:14:44 +03:00
if ( old_index = = - 1 | | new_index = = - 1 | | old_index = = new_index )
return ;
2006-12-19 23:58:55 +03:00
2015-01-06 18:39:14 +03:00
cpufreq_stats_update ( stats ) ;
2015-01-06 18:39:07 +03:00
stats - > last_index = new_index ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_FREQ_STAT_DETAILS
2015-01-06 18:39:07 +03:00
stats - > trans_table [ old_index * stats - > max_state + new_index ] + + ;
2005-04-17 02:20:36 +04:00
# endif
2015-01-06 18:39:07 +03:00
stats - > total_trans + + ;
2005-04-17 02:20:36 +04:00
}