1 // SPDX-License-Identifier: GPL-2.0-only
3 * drivers/cpufreq/cpufreq_stats.c
5 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
6 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
10 #include <linux/cpufreq.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
15 struct cpufreq_stats
{
16 unsigned int total_trans
;
17 unsigned long long last_time
;
18 unsigned int max_state
;
19 unsigned int state_num
;
20 unsigned int last_index
;
23 unsigned int *freq_table
;
24 unsigned int *trans_table
;
27 static void cpufreq_stats_update(struct cpufreq_stats
*stats
)
29 unsigned long long cur_time
= get_jiffies_64();
31 stats
->time_in_state
[stats
->last_index
] += cur_time
- stats
->last_time
;
32 stats
->last_time
= cur_time
;
35 static void cpufreq_stats_clear_table(struct cpufreq_stats
*stats
)
37 unsigned int count
= stats
->max_state
;
39 spin_lock(&stats
->lock
);
40 memset(stats
->time_in_state
, 0, count
* sizeof(u64
));
41 memset(stats
->trans_table
, 0, count
* count
* sizeof(int));
42 stats
->last_time
= get_jiffies_64();
43 stats
->total_trans
= 0;
44 spin_unlock(&stats
->lock
);
47 static ssize_t
show_total_trans(struct cpufreq_policy
*policy
, char *buf
)
49 return sprintf(buf
, "%d\n", policy
->stats
->total_trans
);
51 cpufreq_freq_attr_ro(total_trans
);
53 static ssize_t
show_time_in_state(struct cpufreq_policy
*policy
, char *buf
)
55 struct cpufreq_stats
*stats
= policy
->stats
;
59 if (policy
->fast_switch_enabled
)
62 spin_lock(&stats
->lock
);
63 cpufreq_stats_update(stats
);
64 spin_unlock(&stats
->lock
);
66 for (i
= 0; i
< stats
->state_num
; i
++) {
67 len
+= sprintf(buf
+ len
, "%u %llu\n", stats
->freq_table
[i
],
69 jiffies_64_to_clock_t(stats
->time_in_state
[i
]));
73 cpufreq_freq_attr_ro(time_in_state
);
75 static ssize_t
store_reset(struct cpufreq_policy
*policy
, const char *buf
,
78 /* We don't care what is written to the attribute. */
79 cpufreq_stats_clear_table(policy
->stats
);
82 cpufreq_freq_attr_wo(reset
);
84 static ssize_t
show_trans_table(struct cpufreq_policy
*policy
, char *buf
)
86 struct cpufreq_stats
*stats
= policy
->stats
;
90 if (policy
->fast_switch_enabled
)
93 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " From : To\n");
94 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, " : ");
95 for (i
= 0; i
< stats
->state_num
; i
++) {
98 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
99 stats
->freq_table
[i
]);
101 if (len
>= PAGE_SIZE
)
104 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
106 for (i
= 0; i
< stats
->state_num
; i
++) {
107 if (len
>= PAGE_SIZE
)
110 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u: ",
111 stats
->freq_table
[i
]);
113 for (j
= 0; j
< stats
->state_num
; j
++) {
114 if (len
>= PAGE_SIZE
)
116 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "%9u ",
117 stats
->trans_table
[i
*stats
->max_state
+j
]);
119 if (len
>= PAGE_SIZE
)
121 len
+= snprintf(buf
+ len
, PAGE_SIZE
- len
, "\n");
124 if (len
>= PAGE_SIZE
) {
125 pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n");
130 cpufreq_freq_attr_ro(trans_table
);
132 static struct attribute
*default_attrs
[] = {
139 static const struct attribute_group stats_attr_group
= {
140 .attrs
= default_attrs
,
144 static int freq_table_get_index(struct cpufreq_stats
*stats
, unsigned int freq
)
147 for (index
= 0; index
< stats
->max_state
; index
++)
148 if (stats
->freq_table
[index
] == freq
)
153 void cpufreq_stats_free_table(struct cpufreq_policy
*policy
)
155 struct cpufreq_stats
*stats
= policy
->stats
;
161 pr_debug("%s: Free stats table\n", __func__
);
163 sysfs_remove_group(&policy
->kobj
, &stats_attr_group
);
164 kfree(stats
->time_in_state
);
166 policy
->stats
= NULL
;
169 void cpufreq_stats_create_table(struct cpufreq_policy
*policy
)
171 unsigned int i
= 0, count
= 0, ret
= -ENOMEM
;
172 struct cpufreq_stats
*stats
;
173 unsigned int alloc_size
;
174 struct cpufreq_frequency_table
*pos
;
176 count
= cpufreq_table_count_valid_entries(policy
);
180 /* stats already initialized */
184 stats
= kzalloc(sizeof(*stats
), GFP_KERNEL
);
188 alloc_size
= count
* sizeof(int) + count
* sizeof(u64
);
190 alloc_size
+= count
* count
* sizeof(int);
192 /* Allocate memory for time_in_state/freq_table/trans_table in one go */
193 stats
->time_in_state
= kzalloc(alloc_size
, GFP_KERNEL
);
194 if (!stats
->time_in_state
)
197 stats
->freq_table
= (unsigned int *)(stats
->time_in_state
+ count
);
199 stats
->trans_table
= stats
->freq_table
+ count
;
201 stats
->max_state
= count
;
203 /* Find valid-unique entries */
204 cpufreq_for_each_valid_entry(pos
, policy
->freq_table
)
205 if (freq_table_get_index(stats
, pos
->frequency
) == -1)
206 stats
->freq_table
[i
++] = pos
->frequency
;
208 stats
->state_num
= i
;
209 stats
->last_time
= get_jiffies_64();
210 stats
->last_index
= freq_table_get_index(stats
, policy
->cur
);
211 spin_lock_init(&stats
->lock
);
213 policy
->stats
= stats
;
214 ret
= sysfs_create_group(&policy
->kobj
, &stats_attr_group
);
218 /* We failed, release resources */
219 policy
->stats
= NULL
;
220 kfree(stats
->time_in_state
);
225 void cpufreq_stats_record_transition(struct cpufreq_policy
*policy
,
226 unsigned int new_freq
)
228 struct cpufreq_stats
*stats
= policy
->stats
;
229 int old_index
, new_index
;
232 pr_debug("%s: No stats found\n", __func__
);
236 old_index
= stats
->last_index
;
237 new_index
= freq_table_get_index(stats
, new_freq
);
239 /* We can't do stats->time_in_state[-1]= .. */
240 if (old_index
== -1 || new_index
== -1 || old_index
== new_index
)
243 spin_lock(&stats
->lock
);
244 cpufreq_stats_update(stats
);
246 stats
->last_index
= new_index
;
247 stats
->trans_table
[old_index
* stats
->max_state
+ new_index
]++;
248 stats
->total_trans
++;
249 spin_unlock(&stats
->lock
);