2 * drivers/cpufreq/cpufreq_stats.c
4 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
5 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/cpu.h>
13 #include <linux/cpufreq.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/cputime.h>
18 static DEFINE_SPINLOCK(cpufreq_stats_lock
);
20 struct cpufreq_stats
{
21 unsigned int total_trans
;
22 unsigned long long last_time
;
23 unsigned int max_state
;
24 unsigned int state_num
;
25 unsigned int last_index
;
27 unsigned int *freq_table
;
28 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
29 unsigned int *trans_table
;
33 static int cpufreq_stats_update(struct cpufreq_stats
*stats
)
35 unsigned long long cur_time
= get_jiffies_64();
37 spin_lock(&cpufreq_stats_lock
);
38 stats
->time_in_state
[stats
->last_index
] += cur_time
- stats
->last_time
;
39 stats
->last_time
= cur_time
;
40 spin_unlock(&cpufreq_stats_lock
);
44 static void cpufreq_stats_clear_table(struct cpufreq_stats
*stats
)
46 unsigned int count
= stats
->max_state
;
48 memset(stats
->time_in_state
, 0, count
* sizeof(u64
));
49 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
50 memset(stats
->trans_table
, 0, count
* count
* sizeof(int));
52 stats
->last_time
= get_jiffies_64();
53 stats
->total_trans
= 0;
56 static ssize_t
show_total_trans(struct cpufreq_policy
*policy
, char *buf
)
58 return sprintf(buf
, "%d\n", policy
->stats
->total_trans
);
61 static ssize_t
show_time_in_state(struct cpufreq_policy
*policy
, char *buf
)
63 struct cpufreq_stats
*stats
= policy
->stats
;
67 if (policy
->fast_switch_enabled
)
70 cpufreq_stats_update(stats
);
71 for (i
= 0; i
< stats
->state_num
; i
++) {
72 len
+= sprintf(buf
+ len
, "%u %llu\n", stats
->freq_table
[i
],
74 jiffies_64_to_clock_t(stats
->time_in_state
[i
]));
79 static ssize_t
store_reset(struct cpufreq_policy
*policy
, const char *buf
,
82 /* We don't care what is written to the attribute. */
83 cpufreq_stats_clear_table(policy
->stats
);
87 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
88 static ssize_t
show_trans_table(struct cpufreq_policy
*policy
, char *buf
)
90 struct cpufreq_stats
*stats
= policy
->stats
;
94 if (policy
->fast_switch_enabled
)
97 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " From : To\n");
98 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " : ");
99 for (i
= 0; i
< stats
->state_num
; i
++) {
100 if (len
>= PAGE_SIZE
)
102 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
103 stats
->freq_table
[i
]);
105 if (len
>= PAGE_SIZE
)
108 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
110 for (i
= 0; i
< stats
->state_num
; i
++) {
111 if (len
>= PAGE_SIZE
)
114 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u: ",
115 stats
->freq_table
[i
]);
117 for (j
= 0; j
< stats
->state_num
; j
++) {
118 if (len
>= PAGE_SIZE
)
120 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
121 stats
->trans_table
[i
*stats
->max_state
+j
]);
123 if (len
>= PAGE_SIZE
)
125 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
127 if (len
>= PAGE_SIZE
)
131 cpufreq_freq_attr_ro(trans_table
);
134 cpufreq_freq_attr_ro(total_trans
);
135 cpufreq_freq_attr_ro(time_in_state
);
136 cpufreq_freq_attr_wo(reset
);
138 static struct attribute
*default_attrs
[] = {
142 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
147 static struct attribute_group stats_attr_group
= {
148 .attrs
= default_attrs
,
152 static int freq_table_get_index(struct cpufreq_stats
*stats
, unsigned int freq
)
155 for (index
= 0; index
< stats
->max_state
; index
++)
156 if (stats
->freq_table
[index
] == freq
)
161 void cpufreq_stats_free_table(struct cpufreq_policy
*policy
)
163 struct cpufreq_stats
*stats
= policy
->stats
;
169 pr_debug("%s: Free stats table\n", __func__
);
171 sysfs_remove_group(&policy
->kobj
, &stats_attr_group
);
172 kfree(stats
->time_in_state
);
174 policy
->stats
= NULL
;
177 void cpufreq_stats_create_table(struct cpufreq_policy
*policy
)
179 unsigned int i
= 0, count
= 0, ret
= -ENOMEM
;
180 struct cpufreq_stats
*stats
;
181 unsigned int alloc_size
;
182 struct cpufreq_frequency_table
*pos
, *table
;
184 /* We need cpufreq table for creating stats table */
185 table
= policy
->freq_table
;
186 if (unlikely(!table
))
189 /* stats already initialized */
193 stats
= kzalloc(sizeof(*stats
), GFP_KERNEL
);
197 /* Find total allocation size */
198 cpufreq_for_each_valid_entry(pos
, table
)
201 alloc_size
= count
* sizeof(int) + count
* sizeof(u64
);
203 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
204 alloc_size
+= count
* count
* sizeof(int);
207 /* Allocate memory for time_in_state/freq_table/trans_table in one go */
208 stats
->time_in_state
= kzalloc(alloc_size
, GFP_KERNEL
);
209 if (!stats
->time_in_state
)
212 stats
->freq_table
= (unsigned int *)(stats
->time_in_state
+ count
);
214 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
215 stats
->trans_table
= stats
->freq_table
+ count
;
218 stats
->max_state
= count
;
220 /* Find valid-unique entries */
221 cpufreq_for_each_valid_entry(pos
, table
)
222 if (freq_table_get_index(stats
, pos
->frequency
) == -1)
223 stats
->freq_table
[i
++] = pos
->frequency
;
225 stats
->state_num
= i
;
226 stats
->last_time
= get_jiffies_64();
227 stats
->last_index
= freq_table_get_index(stats
, policy
->cur
);
229 policy
->stats
= stats
;
230 ret
= sysfs_create_group(&policy
->kobj
, &stats_attr_group
);
234 /* We failed, release resources */
235 policy
->stats
= NULL
;
236 kfree(stats
->time_in_state
);
241 void cpufreq_stats_record_transition(struct cpufreq_policy
*policy
,
242 unsigned int new_freq
)
244 struct cpufreq_stats
*stats
= policy
->stats
;
245 int old_index
, new_index
;
248 pr_debug("%s: No stats found\n", __func__
);
252 old_index
= stats
->last_index
;
253 new_index
= freq_table_get_index(stats
, new_freq
);
255 /* We can't do stats->time_in_state[-1]= .. */
256 if (old_index
== -1 || new_index
== -1 || old_index
== new_index
)
259 cpufreq_stats_update(stats
);
261 stats
->last_index
= new_index
;
262 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
263 stats
->trans_table
[old_index
* stats
->max_state
+ new_index
]++;
265 stats
->total_trans
++;