]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/cpufreq/cpufreq_stats.c
Merge branches 'for-4.11/upstream-fixes', 'for-4.12/accutouch', 'for-4.12/cp2112...
[mirror_ubuntu-artful-kernel.git] / drivers / cpufreq / cpufreq_stats.c
CommitLineData
1da177e4
LT
1/*
2 * drivers/cpufreq/cpufreq_stats.c
3 *
4 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
0a829c5a 5 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
1da177e4
LT
6 *
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.
10 */
11
1da177e4 12#include <linux/cpu.h>
1da177e4 13#include <linux/cpufreq.h>
5c720d37 14#include <linux/module.h>
5ff0a268 15#include <linux/slab.h>
1da177e4 16
1aefc75b 17static DEFINE_SPINLOCK(cpufreq_stats_lock);
1da177e4 18
1da177e4 19struct cpufreq_stats {
1da177e4 20 unsigned int total_trans;
bb176f7d 21 unsigned long long last_time;
1da177e4
LT
22 unsigned int max_state;
23 unsigned int state_num;
24 unsigned int last_index;
1e7586a1 25 u64 *time_in_state;
1da177e4 26 unsigned int *freq_table;
1da177e4 27 unsigned int *trans_table;
1da177e4
LT
28};
29
50941607 30static int cpufreq_stats_update(struct cpufreq_stats *stats)
1da177e4 31{
9531347c 32 unsigned long long cur_time = get_jiffies_64();
58f1df25 33
1da177e4 34 spin_lock(&cpufreq_stats_lock);
c960f9b2 35 stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
50941607 36 stats->last_time = cur_time;
1da177e4
LT
37 spin_unlock(&cpufreq_stats_lock);
38 return 0;
39}
40
ee7930ee
MM
41static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
42{
43 unsigned int count = stats->max_state;
44
45 memset(stats->time_in_state, 0, count * sizeof(u64));
ee7930ee 46 memset(stats->trans_table, 0, count * count * sizeof(int));
ee7930ee
MM
47 stats->last_time = get_jiffies_64();
48 stats->total_trans = 0;
49}
50
0a829c5a 51static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
1da177e4 52{
a9aaf291 53 return sprintf(buf, "%d\n", policy->stats->total_trans);
1da177e4
LT
54}
55
0a829c5a 56static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
1da177e4 57{
50941607 58 struct cpufreq_stats *stats = policy->stats;
1da177e4
LT
59 ssize_t len = 0;
60 int i;
a9aaf291 61
1aefc75b
RW
62 if (policy->fast_switch_enabled)
63 return 0;
64
50941607
VK
65 cpufreq_stats_update(stats);
66 for (i = 0; i < stats->state_num; i++) {
67 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
0a829c5a 68 (unsigned long long)
50941607 69 jiffies_64_to_clock_t(stats->time_in_state[i]));
1da177e4
LT
70 }
71 return len;
72}
73
ee7930ee
MM
74static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
75 size_t count)
76{
77 /* We don't care what is written to the attribute. */
78 cpufreq_stats_clear_table(policy->stats);
79 return count;
80}
81
0a829c5a 82static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
1da177e4 83{
50941607 84 struct cpufreq_stats *stats = policy->stats;
1da177e4
LT
85 ssize_t len = 0;
86 int i, j;
87
1aefc75b
RW
88 if (policy->fast_switch_enabled)
89 return 0;
90
58f1df25
VP
91 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
92 len += snprintf(buf + len, PAGE_SIZE - len, " : ");
50941607 93 for (i = 0; i < stats->state_num; i++) {
58f1df25
VP
94 if (len >= PAGE_SIZE)
95 break;
96 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
50941607 97 stats->freq_table[i]);
58f1df25
VP
98 }
99 if (len >= PAGE_SIZE)
25aca347 100 return PAGE_SIZE;
58f1df25
VP
101
102 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
103
50941607 104 for (i = 0; i < stats->state_num; i++) {
1da177e4
LT
105 if (len >= PAGE_SIZE)
106 break;
58f1df25
VP
107
108 len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
50941607 109 stats->freq_table[i]);
1da177e4 110
50941607 111 for (j = 0; j < stats->state_num; j++) {
1da177e4
LT
112 if (len >= PAGE_SIZE)
113 break;
58f1df25 114 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
50941607 115 stats->trans_table[i*stats->max_state+j]);
1da177e4 116 }
25aca347
CEB
117 if (len >= PAGE_SIZE)
118 break;
1da177e4
LT
119 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
120 }
25aca347
CEB
121 if (len >= PAGE_SIZE)
122 return PAGE_SIZE;
1da177e4
LT
123 return len;
124}
df18e504 125cpufreq_freq_attr_ro(trans_table);
1da177e4 126
df18e504
VK
127cpufreq_freq_attr_ro(total_trans);
128cpufreq_freq_attr_ro(time_in_state);
ee7930ee 129cpufreq_freq_attr_wo(reset);
1da177e4
LT
130
131static struct attribute *default_attrs[] = {
df18e504
VK
132 &total_trans.attr,
133 &time_in_state.attr,
ee7930ee 134 &reset.attr,
df18e504 135 &trans_table.attr,
1da177e4
LT
136 NULL
137};
138static struct attribute_group stats_attr_group = {
139 .attrs = default_attrs,
140 .name = "stats"
141};
142
50941607 143static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq)
1da177e4
LT
144{
145 int index;
50941607
VK
146 for (index = 0; index < stats->max_state; index++)
147 if (stats->freq_table[index] == freq)
1da177e4
LT
148 return index;
149 return -1;
150}
151
1aefc75b 152void cpufreq_stats_free_table(struct cpufreq_policy *policy)
1da177e4 153{
50941607 154 struct cpufreq_stats *stats = policy->stats;
b8eed8af 155
a9aaf291 156 /* Already freed */
50941607 157 if (!stats)
2d13594d
VK
158 return;
159
50941607 160 pr_debug("%s: Free stats table\n", __func__);
2d13594d
VK
161
162 sysfs_remove_group(&policy->kobj, &stats_attr_group);
50941607
VK
163 kfree(stats->time_in_state);
164 kfree(stats);
a9aaf291 165 policy->stats = NULL;
98586ed8 166}
167
1aefc75b 168void cpufreq_stats_create_table(struct cpufreq_policy *policy)
1da177e4 169{
a685c6d0 170 unsigned int i = 0, count = 0, ret = -ENOMEM;
50941607 171 struct cpufreq_stats *stats;
1da177e4 172 unsigned int alloc_size;
041526f9 173 struct cpufreq_frequency_table *pos, *table;
ad4c2302 174
a685c6d0 175 /* We need cpufreq table for creating stats table */
f8bfc116 176 table = policy->freq_table;
ad4c2302 177 if (unlikely(!table))
1aefc75b 178 return;
ad4c2302 179
b8c67448 180 /* stats already initialized */
a9aaf291 181 if (policy->stats)
1aefc75b 182 return;
b8c67448 183
50941607 184 stats = kzalloc(sizeof(*stats), GFP_KERNEL);
a685c6d0 185 if (!stats)
1aefc75b 186 return;
1da177e4 187
a685c6d0 188 /* Find total allocation size */
041526f9 189 cpufreq_for_each_valid_entry(pos, table)
1da177e4 190 count++;
1da177e4 191
1e7586a1 192 alloc_size = count * sizeof(int) + count * sizeof(u64);
1da177e4 193
1da177e4 194 alloc_size += count * count * sizeof(int);
a685c6d0
VK
195
196 /* Allocate memory for time_in_state/freq_table/trans_table in one go */
50941607 197 stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
a685c6d0
VK
198 if (!stats->time_in_state)
199 goto free_stat;
200
50941607 201 stats->freq_table = (unsigned int *)(stats->time_in_state + count);
1da177e4 202
50941607 203 stats->trans_table = stats->freq_table + count;
a685c6d0
VK
204
205 stats->max_state = count;
206
207 /* Find valid-unique entries */
041526f9 208 cpufreq_for_each_valid_entry(pos, table)
50941607
VK
209 if (freq_table_get_index(stats, pos->frequency) == -1)
210 stats->freq_table[i++] = pos->frequency;
a685c6d0 211
490285c6 212 stats->state_num = i;
50941607
VK
213 stats->last_time = get_jiffies_64();
214 stats->last_index = freq_table_get_index(stats, policy->cur);
a685c6d0
VK
215
216 policy->stats = stats;
217 ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
218 if (!ret)
1aefc75b 219 return;
a685c6d0
VK
220
221 /* We failed, release resources */
a9aaf291 222 policy->stats = NULL;
a685c6d0
VK
223 kfree(stats->time_in_state);
224free_stat:
225 kfree(stats);
b3f9ff88
VK
226}
227
1aefc75b
RW
228void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
229 unsigned int new_freq)
1da177e4 230{
1aefc75b 231 struct cpufreq_stats *stats = policy->stats;
1da177e4
LT
232 int old_index, new_index;
233
1aefc75b 234 if (!stats) {
a9aaf291 235 pr_debug("%s: No stats found\n", __func__);
1aefc75b 236 return;
a9aaf291
VK
237 }
238
50941607 239 old_index = stats->last_index;
1aefc75b 240 new_index = freq_table_get_index(stats, new_freq);
1da177e4 241
50941607 242 /* We can't do stats->time_in_state[-1]= .. */
1aefc75b
RW
243 if (old_index == -1 || new_index == -1 || old_index == new_index)
244 return;
8edc59d9 245
e7347694
VK
246 cpufreq_stats_update(stats);
247
50941607 248 stats->last_index = new_index;
50941607 249 stats->trans_table[old_index * stats->max_state + new_index]++;
50941607 250 stats->total_trans++;
1da177e4 251}