Merge branch 'v3.10/topic/arm64-errata' into linux-linaro-lsk-v3.10
[firefly-linux-kernel-4.4.55.git] / drivers / cpufreq / cpufreq_stats.c
1 /*
2  *  drivers/cpufreq/cpufreq_stats.c
3  *
4  *  Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
5  *  (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
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
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
14 #include <linux/cpu.h>
15 #include <linux/sysfs.h>
16 #include <linux/cpufreq.h>
17 #include <linux/module.h>
18 #include <linux/jiffies.h>
19 #include <linux/percpu.h>
20 #include <linux/kobject.h>
21 #include <linux/spinlock.h>
22 #include <linux/notifier.h>
23 #include <asm/cputime.h>
24 #ifdef CONFIG_BL_SWITCHER
25 #include <asm/bL_switcher.h>
26 #endif
27
28 static spinlock_t cpufreq_stats_lock;
29
30 struct cpufreq_stats {
31         unsigned int cpu;
32         unsigned int total_trans;
33         unsigned long long  last_time;
34         unsigned int max_state;
35         unsigned int state_num;
36         unsigned int last_index;
37         u64 *time_in_state;
38         unsigned int *freq_table;
39 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
40         unsigned int *trans_table;
41 #endif
42 };
43
44 static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
45
46 struct cpufreq_stats_attribute {
47         struct attribute attr;
48         ssize_t(*show) (struct cpufreq_stats *, char *);
49 };
50
51 static int cpufreq_stats_update(unsigned int cpu)
52 {
53         struct cpufreq_stats *stat;
54         unsigned long long cur_time;
55
56         cur_time = get_jiffies_64();
57         spin_lock(&cpufreq_stats_lock);
58         stat = per_cpu(cpufreq_stats_table, cpu);
59         if (stat->time_in_state)
60                 stat->time_in_state[stat->last_index] +=
61                         cur_time - stat->last_time;
62         stat->last_time = cur_time;
63         spin_unlock(&cpufreq_stats_lock);
64         return 0;
65 }
66
67 static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
68 {
69         struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
70         if (!stat)
71                 return 0;
72         return sprintf(buf, "%d\n",
73                         per_cpu(cpufreq_stats_table, stat->cpu)->total_trans);
74 }
75
76 static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
77 {
78         ssize_t len = 0;
79         int i;
80         struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
81         if (!stat)
82                 return 0;
83         cpufreq_stats_update(stat->cpu);
84         for (i = 0; i < stat->state_num; i++) {
85                 len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
86                         (unsigned long long)
87                         jiffies_64_to_clock_t(stat->time_in_state[i]));
88         }
89         return len;
90 }
91
92 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
93 static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
94 {
95         ssize_t len = 0;
96         int i, j;
97
98         struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
99         if (!stat)
100                 return 0;
101         cpufreq_stats_update(stat->cpu);
102         len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
103         len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
104         for (i = 0; i < stat->state_num; i++) {
105                 if (len >= PAGE_SIZE)
106                         break;
107                 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
108                                 stat->freq_table[i]);
109         }
110         if (len >= PAGE_SIZE)
111                 return PAGE_SIZE;
112
113         len += snprintf(buf + len, PAGE_SIZE - len, "\n");
114
115         for (i = 0; i < stat->state_num; i++) {
116                 if (len >= PAGE_SIZE)
117                         break;
118
119                 len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
120                                 stat->freq_table[i]);
121
122                 for (j = 0; j < stat->state_num; j++)   {
123                         if (len >= PAGE_SIZE)
124                                 break;
125                         len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
126                                         stat->trans_table[i*stat->max_state+j]);
127                 }
128                 if (len >= PAGE_SIZE)
129                         break;
130                 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
131         }
132         if (len >= PAGE_SIZE)
133                 return PAGE_SIZE;
134         return len;
135 }
136 cpufreq_freq_attr_ro(trans_table);
137 #endif
138
139 cpufreq_freq_attr_ro(total_trans);
140 cpufreq_freq_attr_ro(time_in_state);
141
142 static struct attribute *default_attrs[] = {
143         &total_trans.attr,
144         &time_in_state.attr,
145 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
146         &trans_table.attr,
147 #endif
148         NULL
149 };
150 static struct attribute_group stats_attr_group = {
151         .attrs = default_attrs,
152         .name = "stats"
153 };
154
155 static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
156 {
157         int index;
158         for (index = 0; index < stat->max_state; index++)
159                 if (stat->freq_table[index] == freq)
160                         return index;
161         return -1;
162 }
163
164 /* should be called late in the CPU removal sequence so that the stats
165  * memory is still available in case someone tries to use it.
166  */
167 static void cpufreq_stats_free_table(unsigned int cpu)
168 {
169         struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
170
171         if (stat) {
172                 pr_debug("%s: Free stat table\n", __func__);
173                 kfree(stat->time_in_state);
174                 kfree(stat);
175                 per_cpu(cpufreq_stats_table, cpu) = NULL;
176         }
177 }
178
179 /* must be called early in the CPU removal sequence (before
180  * cpufreq_remove_dev) so that policy is still valid.
181  */
182 static void cpufreq_stats_free_sysfs(unsigned int cpu)
183 {
184         struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
185
186         if (!policy)
187                 return;
188
189         if (!cpufreq_frequency_get_table(cpu))
190                 goto put_ref;
191
192         if (!policy_is_shared(policy)) {
193                 pr_debug("%s: Free sysfs stat\n", __func__);
194                 sysfs_remove_group(&policy->kobj, &stats_attr_group);
195         }
196
197 put_ref:
198         cpufreq_cpu_put(policy);
199 }
200
201 static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
202                 struct cpufreq_frequency_table *table)
203 {
204         unsigned int i, j, count = 0, ret = 0;
205         struct cpufreq_stats *stat;
206         struct cpufreq_policy *data;
207         unsigned int alloc_size;
208         unsigned int cpu = policy->cpu;
209         if (per_cpu(cpufreq_stats_table, cpu))
210                 return -EBUSY;
211         stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
212         if ((stat) == NULL)
213                 return -ENOMEM;
214
215         data = cpufreq_cpu_get(cpu);
216         if (data == NULL) {
217                 ret = -EINVAL;
218                 goto error_get_fail;
219         }
220
221         ret = sysfs_create_group(&data->kobj, &stats_attr_group);
222         if (ret)
223                 goto error_out;
224
225         stat->cpu = cpu;
226         per_cpu(cpufreq_stats_table, cpu) = stat;
227
228         for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
229                 unsigned int freq = table[i].frequency;
230                 if (freq == CPUFREQ_ENTRY_INVALID)
231                         continue;
232                 count++;
233         }
234
235         alloc_size = count * sizeof(int) + count * sizeof(u64);
236
237 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
238         alloc_size += count * count * sizeof(int);
239 #endif
240         stat->max_state = count;
241         stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
242         if (!stat->time_in_state) {
243                 ret = -ENOMEM;
244                 goto error_out;
245         }
246         stat->freq_table = (unsigned int *)(stat->time_in_state + count);
247
248 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
249         stat->trans_table = stat->freq_table + count;
250 #endif
251         j = 0;
252         for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
253                 unsigned int freq = table[i].frequency;
254                 if (freq == CPUFREQ_ENTRY_INVALID)
255                         continue;
256                 if (freq_table_get_index(stat, freq) == -1)
257                         stat->freq_table[j++] = freq;
258         }
259         stat->state_num = j;
260         spin_lock(&cpufreq_stats_lock);
261         stat->last_time = get_jiffies_64();
262         stat->last_index = freq_table_get_index(stat, policy->cur);
263         spin_unlock(&cpufreq_stats_lock);
264         cpufreq_cpu_put(data);
265         return 0;
266 error_out:
267         cpufreq_cpu_put(data);
268 error_get_fail:
269         kfree(stat);
270         per_cpu(cpufreq_stats_table, cpu) = NULL;
271         return ret;
272 }
273
274 static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
275 {
276         struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table,
277                         policy->last_cpu);
278
279         pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n",
280                         policy->cpu, policy->last_cpu);
281         per_cpu(cpufreq_stats_table, policy->cpu) = per_cpu(cpufreq_stats_table,
282                         policy->last_cpu);
283         per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL;
284         stat->cpu = policy->cpu;
285 }
286
287 static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
288                 unsigned long val, void *data)
289 {
290         int ret;
291         struct cpufreq_policy *policy = data;
292         struct cpufreq_frequency_table *table;
293         unsigned int cpu = policy->cpu;
294
295         if (val == CPUFREQ_UPDATE_POLICY_CPU) {
296                 cpufreq_stats_update_policy_cpu(policy);
297                 return 0;
298         }
299
300         if (val != CPUFREQ_NOTIFY)
301                 return 0;
302         table = cpufreq_frequency_get_table(cpu);
303         if (!table)
304                 return 0;
305         ret = cpufreq_stats_create_table(policy, table);
306         if (ret)
307                 return ret;
308         return 0;
309 }
310
311 static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
312                 unsigned long val, void *data)
313 {
314         struct cpufreq_freqs *freq = data;
315         struct cpufreq_stats *stat;
316         int old_index, new_index;
317
318         if (val != CPUFREQ_POSTCHANGE)
319                 return 0;
320
321         stat = per_cpu(cpufreq_stats_table, freq->cpu);
322         if (!stat)
323                 return 0;
324
325         old_index = stat->last_index;
326         new_index = freq_table_get_index(stat, freq->new);
327
328         /* We can't do stat->time_in_state[-1]= .. */
329         if (old_index == -1 || new_index == -1)
330                 return 0;
331
332         cpufreq_stats_update(freq->cpu);
333
334         if (old_index == new_index)
335                 return 0;
336
337         spin_lock(&cpufreq_stats_lock);
338         stat->last_index = new_index;
339 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
340         stat->trans_table[old_index * stat->max_state + new_index]++;
341 #endif
342         stat->total_trans++;
343         spin_unlock(&cpufreq_stats_lock);
344         return 0;
345 }
346
347 static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
348                                                unsigned long action,
349                                                void *hcpu)
350 {
351         unsigned int cpu = (unsigned long)hcpu;
352
353         switch (action) {
354         case CPU_ONLINE:
355         case CPU_ONLINE_FROZEN:
356                 cpufreq_update_policy(cpu);
357                 break;
358         case CPU_DOWN_PREPARE:
359         case CPU_DOWN_PREPARE_FROZEN:
360                 cpufreq_stats_free_sysfs(cpu);
361                 break;
362         case CPU_DEAD:
363         case CPU_DEAD_FROZEN:
364                 cpufreq_stats_free_table(cpu);
365                 break;
366         }
367         return NOTIFY_OK;
368 }
369
370 /* priority=1 so this will get called before cpufreq_remove_dev */
371 static struct notifier_block cpufreq_stat_cpu_notifier __refdata = {
372         .notifier_call = cpufreq_stat_cpu_callback,
373         .priority = 1,
374 };
375
376 static struct notifier_block notifier_policy_block = {
377         .notifier_call = cpufreq_stat_notifier_policy
378 };
379
380 static struct notifier_block notifier_trans_block = {
381         .notifier_call = cpufreq_stat_notifier_trans
382 };
383
384 static int cpufreq_stats_setup(void)
385 {
386         int ret;
387         unsigned int cpu;
388
389         spin_lock_init(&cpufreq_stats_lock);
390         ret = cpufreq_register_notifier(&notifier_policy_block,
391                                 CPUFREQ_POLICY_NOTIFIER);
392         if (ret)
393                 return ret;
394
395         register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
396         for_each_online_cpu(cpu)
397                 cpufreq_update_policy(cpu);
398
399         ret = cpufreq_register_notifier(&notifier_trans_block,
400                                 CPUFREQ_TRANSITION_NOTIFIER);
401         if (ret) {
402                 cpufreq_unregister_notifier(&notifier_policy_block,
403                                 CPUFREQ_POLICY_NOTIFIER);
404                 unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
405                 for_each_online_cpu(cpu)
406                         cpufreq_stats_free_table(cpu);
407                 return ret;
408         }
409
410         return 0;
411 }
412
413 static void cpufreq_stats_cleanup(void)
414 {
415         unsigned int cpu;
416
417         cpufreq_unregister_notifier(&notifier_policy_block,
418                         CPUFREQ_POLICY_NOTIFIER);
419         cpufreq_unregister_notifier(&notifier_trans_block,
420                         CPUFREQ_TRANSITION_NOTIFIER);
421         unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
422         for_each_online_cpu(cpu) {
423                 cpufreq_stats_free_table(cpu);
424                 cpufreq_stats_free_sysfs(cpu);
425         }
426 }
427
428 #ifdef CONFIG_BL_SWITCHER
429 static int cpufreq_stats_switcher_notifier(struct notifier_block *nfb,
430                                         unsigned long action, void *_arg)
431 {
432         switch (action) {
433         case BL_NOTIFY_PRE_ENABLE:
434         case BL_NOTIFY_PRE_DISABLE:
435                 cpufreq_stats_cleanup();
436                 break;
437
438         case BL_NOTIFY_POST_ENABLE:
439         case BL_NOTIFY_POST_DISABLE:
440                 cpufreq_stats_setup();
441                 break;
442
443         default:
444                 return NOTIFY_DONE;
445         }
446
447         return NOTIFY_OK;
448 }
449
450 static struct notifier_block switcher_notifier = {
451         .notifier_call = cpufreq_stats_switcher_notifier,
452 };
453 #endif
454
455 static int __init cpufreq_stats_init(void)
456 {
457         int ret;
458         spin_lock_init(&cpufreq_stats_lock);
459
460         ret = cpufreq_stats_setup();
461 #ifdef CONFIG_BL_SWITCHER
462         if (!ret)
463                 bL_switcher_register_notifier(&switcher_notifier);
464 #endif
465         return ret;
466 }
467
468 static void __exit cpufreq_stats_exit(void)
469 {
470 #ifdef CONFIG_BL_SWITCHER
471         bL_switcher_unregister_notifier(&switcher_notifier);
472 #endif
473         cpufreq_stats_cleanup();
474 }
475
476 MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
477 MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
478                                 "through sysfs filesystem");
479 MODULE_LICENSE("GPL");
480
481 module_init(cpufreq_stats_init);
482 module_exit(cpufreq_stats_exit);