Merge branch 'linux-linaro-lsk' into linux-linaro-lsk-android
[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                         cputime64_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 cpufreq_stats_create_table_cpu(unsigned int cpu)
348 {
349         struct cpufreq_policy *policy;
350         struct cpufreq_frequency_table *table;
351         int ret = -ENODEV;
352
353         policy = cpufreq_cpu_get(cpu);
354         if (!policy)
355                 return -ENODEV;
356
357         table = cpufreq_frequency_get_table(cpu);
358         if (!table)
359                 goto out;
360
361         ret = cpufreq_stats_create_table(policy, table);
362
363 out:
364         cpufreq_cpu_put(policy);
365         return ret;
366 }
367
368 static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
369                                                unsigned long action,
370                                                void *hcpu)
371 {
372         unsigned int cpu = (unsigned long)hcpu;
373
374         switch (action) {
375         case CPU_ONLINE:
376         case CPU_ONLINE_FROZEN:
377                 cpufreq_update_policy(cpu);
378                 break;
379         case CPU_DOWN_PREPARE:
380         case CPU_DOWN_PREPARE_FROZEN:
381                 cpufreq_stats_free_sysfs(cpu);
382                 break;
383         case CPU_DEAD:
384         case CPU_DEAD_FROZEN:
385                 cpufreq_stats_free_table(cpu);
386                 break;
387         case CPU_DOWN_FAILED:
388         case CPU_DOWN_FAILED_FROZEN:
389                 cpufreq_stats_create_table_cpu(cpu);
390                 break;
391         }
392         return NOTIFY_OK;
393 }
394
395 /* priority=1 so this will get called before cpufreq_remove_dev */
396 static struct notifier_block cpufreq_stat_cpu_notifier __refdata = {
397         .notifier_call = cpufreq_stat_cpu_callback,
398         .priority = 1,
399 };
400
401 static struct notifier_block notifier_policy_block = {
402         .notifier_call = cpufreq_stat_notifier_policy
403 };
404
405 static struct notifier_block notifier_trans_block = {
406         .notifier_call = cpufreq_stat_notifier_trans
407 };
408
409 static int cpufreq_stats_setup(void)
410 {
411         int ret;
412         unsigned int cpu;
413
414         spin_lock_init(&cpufreq_stats_lock);
415         ret = cpufreq_register_notifier(&notifier_policy_block,
416                                 CPUFREQ_POLICY_NOTIFIER);
417         if (ret)
418                 return ret;
419
420         register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
421         for_each_online_cpu(cpu)
422                 cpufreq_update_policy(cpu);
423
424         ret = cpufreq_register_notifier(&notifier_trans_block,
425                                 CPUFREQ_TRANSITION_NOTIFIER);
426         if (ret) {
427                 cpufreq_unregister_notifier(&notifier_policy_block,
428                                 CPUFREQ_POLICY_NOTIFIER);
429                 unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
430                 for_each_online_cpu(cpu)
431                         cpufreq_stats_free_table(cpu);
432                 return ret;
433         }
434
435         return 0;
436 }
437
438 static void cpufreq_stats_cleanup(void)
439 {
440         unsigned int cpu;
441
442         cpufreq_unregister_notifier(&notifier_policy_block,
443                         CPUFREQ_POLICY_NOTIFIER);
444         cpufreq_unregister_notifier(&notifier_trans_block,
445                         CPUFREQ_TRANSITION_NOTIFIER);
446         unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
447         for_each_online_cpu(cpu) {
448                 cpufreq_stats_free_table(cpu);
449                 cpufreq_stats_free_sysfs(cpu);
450         }
451 }
452
453 #ifdef CONFIG_BL_SWITCHER
454 static int cpufreq_stats_switcher_notifier(struct notifier_block *nfb,
455                                         unsigned long action, void *_arg)
456 {
457         switch (action) {
458         case BL_NOTIFY_PRE_ENABLE:
459         case BL_NOTIFY_PRE_DISABLE:
460                 cpufreq_stats_cleanup();
461                 break;
462
463         case BL_NOTIFY_POST_ENABLE:
464         case BL_NOTIFY_POST_DISABLE:
465                 cpufreq_stats_setup();
466                 break;
467
468         default:
469                 return NOTIFY_DONE;
470         }
471
472         return NOTIFY_OK;
473 }
474
475 static struct notifier_block switcher_notifier = {
476         .notifier_call = cpufreq_stats_switcher_notifier,
477 };
478 #endif
479
480 static int __init cpufreq_stats_init(void)
481 {
482         int ret;
483         spin_lock_init(&cpufreq_stats_lock);
484
485         ret = cpufreq_stats_setup();
486 #ifdef CONFIG_BL_SWITCHER
487         if (!ret)
488                 bL_switcher_register_notifier(&switcher_notifier);
489 #endif
490         return ret;
491 }
492
493 static void __exit cpufreq_stats_exit(void)
494 {
495 #ifdef CONFIG_BL_SWITCHER
496         bL_switcher_unregister_notifier(&switcher_notifier);
497 #endif
498         cpufreq_stats_cleanup();
499 }
500
501 MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
502 MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
503                                 "through sysfs filesystem");
504 MODULE_LICENSE("GPL");
505
506 module_init(cpufreq_stats_init);
507 module_exit(cpufreq_stats_exit);