blob: 74a86ec89ddf472abdd87f2105ac43228374589a [file] [log] [blame]
Mahesh Sivasubramanianc2ea76f2016-02-01 10:40:26 -07001/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/spinlock.h>
18#include <linux/uaccess.h>
19#include <linux/proc_fs.h>
20#include <linux/seq_file.h>
21#include <linux/debugfs.h>
22#include <linux/sched.h>
23#include <linux/slab.h>
24#include <linux/suspend.h>
25#include <soc/qcom/spm.h>
26#include <soc/qcom/pm.h>
27#include <soc/qcom/lpm-stats.h>
28
29#define MAX_STR_LEN 256
30#define MAX_TIME_LEN 20
31const char *lpm_stats_reset = "reset";
32const char *lpm_stats_suspend = "suspend";
33
34struct lpm_sleep_time {
35 struct kobj_attribute ts_attr;
36 unsigned int cpu;
37};
38
39struct level_stats {
40 const char *name;
41 struct lpm_stats *owner;
42 int64_t first_bucket_time;
43 int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
44 int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
45 int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
46 int success_count;
47 int failed_count;
48 int64_t total_time;
49 uint64_t enter_time;
50};
51
52static struct level_stats suspend_time_stats;
53
54static DEFINE_PER_CPU_SHARED_ALIGNED(struct lpm_stats, cpu_stats);
55
56static uint64_t get_total_sleep_time(unsigned int cpu_id)
57{
58 struct lpm_stats *stats = &per_cpu(cpu_stats, cpu_id);
59 int i;
60 uint64_t ret = 0;
61
62 for (i = 0; i < stats->num_levels; i++)
63 ret += stats->time_stats[i].total_time;
64
65 return ret;
66}
67
68static void update_level_stats(struct level_stats *stats, uint64_t t,
69 bool success)
70{
71 uint64_t bt;
72 int i;
73
74 if (!success) {
75 stats->failed_count++;
76 return;
77 }
78
79 stats->success_count++;
80 stats->total_time += t;
81 bt = t;
82 do_div(bt, stats->first_bucket_time);
83
84 if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
85 (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
86 i = DIV_ROUND_UP(fls((uint32_t)bt),
87 CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
88 else
89 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
90
91 if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT)
92 i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
93
94 stats->bucket[i]++;
95
96 if (t < stats->min_time[i] || !stats->max_time[i])
97 stats->min_time[i] = t;
98 if (t > stats->max_time[i])
99 stats->max_time[i] = t;
100}
101
102static void level_stats_print(struct seq_file *m, struct level_stats *stats)
103{
104 int i = 0;
105 int64_t bucket_time = 0;
106 char seqs[MAX_STR_LEN] = {0};
107 int64_t s = stats->total_time;
108 uint32_t ns = do_div(s, NSEC_PER_SEC);
109
110 snprintf(seqs, MAX_STR_LEN,
111 "[%s] %s:\n"
112 " success count: %7d\n"
113 " total success time: %lld.%09u\n",
114 stats->owner->name,
115 stats->name,
116 stats->success_count,
117 s, ns);
118 seq_puts(m, seqs);
119
120 if (stats->failed_count) {
121 snprintf(seqs, MAX_STR_LEN, " failed count: %7d\n",
122 stats->failed_count);
123 seq_puts(m, seqs);
124 }
125
126 bucket_time = stats->first_bucket_time;
127 for (i = 0;
128 i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
129 i++) {
130 s = bucket_time;
131 ns = do_div(s, NSEC_PER_SEC);
132 snprintf(seqs, MAX_STR_LEN,
133 "\t<%6lld.%09u: %7d (%lld-%lld)\n",
134 s, ns, stats->bucket[i],
135 stats->min_time[i],
136 stats->max_time[i]);
137 seq_puts(m, seqs);
138 bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
139 }
140 snprintf(seqs, MAX_STR_LEN,
141 "\t>=%5lld.%09u:%8d (%lld-%lld)\n",
142 s, ns, stats->bucket[i],
143 stats->min_time[i],
144 stats->max_time[i]);
145 seq_puts(m, seqs);
146}
147
148static int level_stats_file_show(struct seq_file *m, void *v)
149{
150 struct level_stats *stats = NULL;
151
152 if (!m->private)
153 return -EINVAL;
154
155 stats = (struct level_stats *) m->private;
156
157 level_stats_print(m, stats);
158
159 return 0;
160}
161
162static int level_stats_file_open(struct inode *inode, struct file *file)
163{
164 return single_open(file, level_stats_file_show, inode->i_private);
165}
166
167static void level_stats_print_all(struct seq_file *m, struct lpm_stats *stats)
168{
169 struct list_head *centry = NULL;
170 struct lpm_stats *pos = NULL;
171 int i = 0;
172
173 for (i = 0; i < stats->num_levels; i++)
174 level_stats_print(m, &stats->time_stats[i]);
175
176 if (list_empty(&stats->child))
177 return;
178
179 centry = &stats->child;
180 list_for_each_entry(pos, centry, sibling) {
181 level_stats_print_all(m, pos);
182 }
183}
184
185static void level_stats_reset(struct level_stats *stats)
186{
187 memset(stats->bucket, 0, sizeof(stats->bucket));
188 memset(stats->min_time, 0, sizeof(stats->min_time));
189 memset(stats->max_time, 0, sizeof(stats->max_time));
190 stats->success_count = 0;
191 stats->failed_count = 0;
192 stats->total_time = 0;
193}
194
195static void level_stats_reset_all(struct lpm_stats *stats)
196{
197 struct list_head *centry = NULL;
198 struct lpm_stats *pos = NULL;
199 int i = 0;
200
201 for (i = 0; i < stats->num_levels; i++)
202 level_stats_reset(&stats->time_stats[i]);
203
204 if (list_empty(&stats->child))
205 return;
206
207 centry = &stats->child;
208 list_for_each_entry(pos, centry, sibling) {
209 level_stats_reset_all(pos);
210 }
211}
212
213static int lpm_stats_file_show(struct seq_file *m, void *v)
214{
215 struct lpm_stats *stats = (struct lpm_stats *)m->private;
216
217 if (!m->private) {
218 pr_err("%s: Invalid pdata, Cannot print stats\n", __func__);
219 return -EINVAL;
220 }
221
222 level_stats_print_all(m, stats);
223 level_stats_print(m, &suspend_time_stats);
224
225 return 0;
226}
227
228static int lpm_stats_file_open(struct inode *inode, struct file *file)
229{
230 return single_open(file, lpm_stats_file_show, inode->i_private);
231}
232
233static ssize_t level_stats_file_write(struct file *file,
234 const char __user *buffer, size_t count, loff_t *off)
235{
236 char buf[MAX_STR_LEN] = {0};
237 struct inode *in = file->f_inode;
238 struct level_stats *stats = (struct level_stats *)in->i_private;
239 size_t len = strnlen(lpm_stats_reset, MAX_STR_LEN);
240
241 if (!stats)
242 return -EINVAL;
243
244 if (count != len+1)
245 return -EINVAL;
246
247 if (copy_from_user(buf, buffer, len))
248 return -EFAULT;
249
250 if (strcmp(buf, lpm_stats_reset))
251 return -EINVAL;
252
253 level_stats_reset(stats);
254
255 return count;
256}
257
258static ssize_t lpm_stats_file_write(struct file *file,
259 const char __user *buffer, size_t count, loff_t *off)
260{
261 char buf[MAX_STR_LEN] = {0};
262 struct inode *in = file->f_inode;
263 struct lpm_stats *stats = (struct lpm_stats *)in->i_private;
264 size_t len = strnlen(lpm_stats_reset, MAX_STR_LEN);
265
266 if (!stats)
267 return -EINVAL;
268
269 if (count != len+1)
270 return -EINVAL;
271
272 if (copy_from_user(buf, buffer, len))
273 return -EFAULT;
274
275 if (strcmp(buf, lpm_stats_reset))
276 return -EINVAL;
277
278 level_stats_reset_all(stats);
279
280 return count;
281}
282
283int lifo_stats_file_show(struct seq_file *m, void *v)
284{
285 struct lpm_stats *stats = NULL;
286 struct list_head *centry = NULL;
287 struct lpm_stats *pos = NULL;
288 char seqs[MAX_STR_LEN] = {0};
289
290 if (!m->private)
291 return -EINVAL;
292
293 stats = (struct lpm_stats *)m->private;
294
295 if (list_empty(&stats->child)) {
296 pr_err("%s: ERROR: Lifo level with no children.\n",
297 __func__);
298 return -EINVAL;
299 }
300
301 centry = &stats->child;
302 list_for_each_entry(pos, centry, sibling) {
303 snprintf(seqs, MAX_STR_LEN,
304 "%s:\n"
305 "\tLast-In:%u\n"
306 "\tFirst-Out:%u\n",
307 pos->name,
308 pos->lifo.last_in,
309 pos->lifo.first_out);
310 seq_puts(m, seqs);
311 }
312 return 0;
313}
314
315static int lifo_stats_file_open(struct inode *inode, struct file *file)
316{
317 return single_open(file, lifo_stats_file_show, inode->i_private);
318}
319
320static void lifo_stats_reset_all(struct lpm_stats *stats)
321{
322 struct list_head *centry = NULL;
323 struct lpm_stats *pos = NULL;
324
325 centry = &stats->child;
326 list_for_each_entry(pos, centry, sibling) {
327 pos->lifo.last_in = 0;
328 pos->lifo.first_out = 0;
329 if (!list_empty(&pos->child))
330 lifo_stats_reset_all(pos);
331 }
332}
333
334static ssize_t lifo_stats_file_write(struct file *file,
335 const char __user *buffer, size_t count, loff_t *off)
336{
337 char buf[MAX_STR_LEN] = {0};
338 struct inode *in = file->f_inode;
339 struct lpm_stats *stats = (struct lpm_stats *)in->i_private;
340 size_t len = strnlen(lpm_stats_reset, MAX_STR_LEN);
341
342 if (!stats)
343 return -EINVAL;
344
345 if (count != len+1)
346 return -EINVAL;
347
348 if (copy_from_user(buf, buffer, len))
349 return -EFAULT;
350
351 if (strcmp(buf, lpm_stats_reset))
352 return -EINVAL;
353
354 lifo_stats_reset_all(stats);
355
356 return count;
357}
358
359static const struct file_operations level_stats_fops = {
360 .owner = THIS_MODULE,
361 .open = level_stats_file_open,
362 .read = seq_read,
363 .release = single_release,
364 .llseek = no_llseek,
365 .write = level_stats_file_write,
366};
367
368static const struct file_operations lpm_stats_fops = {
369 .owner = THIS_MODULE,
370 .open = lpm_stats_file_open,
371 .read = seq_read,
372 .release = single_release,
373 .llseek = no_llseek,
374 .write = lpm_stats_file_write,
375};
376
377static const struct file_operations lifo_stats_fops = {
378 .owner = THIS_MODULE,
379 .open = lifo_stats_file_open,
380 .read = seq_read,
381 .release = single_release,
382 .llseek = no_llseek,
383 .write = lifo_stats_file_write,
384};
385
386static void update_last_in_stats(struct lpm_stats *stats)
387{
388 struct list_head *centry = NULL;
389 struct lpm_stats *pos = NULL;
390
391 if (list_empty(&stats->child))
392 return;
393
394 centry = &stats->child;
395 list_for_each_entry(pos, centry, sibling) {
396 if (cpumask_test_cpu(smp_processor_id(), &pos->mask)) {
397 pos->lifo.last_in++;
398 return;
399 }
400 }
401 WARN(1, "Should not reach here\n");
402}
403
404static void update_first_out_stats(struct lpm_stats *stats)
405{
406 struct list_head *centry = NULL;
407 struct lpm_stats *pos = NULL;
408
409 if (list_empty(&stats->child))
410 return;
411
412 centry = &stats->child;
413 list_for_each_entry(pos, centry, sibling) {
414 if (cpumask_test_cpu(smp_processor_id(), &pos->mask)) {
415 pos->lifo.first_out++;
416 return;
417 }
418 }
419 WARN(1, "Should not reach here\n");
420}
421
422static inline void update_exit_stats(struct lpm_stats *stats, uint32_t index,
423 bool success)
424{
425 uint64_t exit_time = 0;
426
427 /* Update time stats only when exit is preceded by enter */
428 exit_time = stats->sleep_time;
429 update_level_stats(&stats->time_stats[index], exit_time,
430 success);
431}
432
433static int config_level(const char *name, const char **levels,
434 int num_levels, struct lpm_stats *parent, struct lpm_stats *stats)
435{
436 int i = 0;
437 struct dentry *directory = NULL;
438 const char *rootname = "lpm_stats";
439 const char *dirname = rootname;
440
441 strlcpy(stats->name, name, MAX_STR_LEN);
442 stats->num_levels = num_levels;
443 stats->parent = parent;
444 INIT_LIST_HEAD(&stats->sibling);
445 INIT_LIST_HEAD(&stats->child);
446
447 stats->time_stats = kcalloc(num_levels, sizeof(struct level_stats),
448 GFP_KERNEL);
449 if (!stats->time_stats)
450 return -ENOMEM;
451
452 if (parent) {
453 list_add_tail(&stats->sibling, &parent->child);
454 directory = parent->directory;
455 dirname = name;
456 }
457
458 stats->directory = debugfs_create_dir(dirname, directory);
459 if (!stats->directory) {
460 pr_err("%s: Unable to create %s debugfs directory\n",
461 __func__, dirname);
462 kfree(stats->time_stats);
463 return -EPERM;
464 }
465
466 for (i = 0; i < num_levels; i++) {
467 stats->time_stats[i].name = levels[i];
468 stats->time_stats[i].owner = stats;
469 stats->time_stats[i].first_bucket_time =
470 CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
471 stats->time_stats[i].enter_time = 0;
472
473 if (!debugfs_create_file(stats->time_stats[i].name, 0444,
474 stats->directory, (void *)&stats->time_stats[i],
475 &level_stats_fops)) {
476 pr_err("%s: Unable to create %s %s level-stats file\n",
477 __func__, stats->name,
478 stats->time_stats[i].name);
479 kfree(stats->time_stats);
480 return -EPERM;
481 }
482 }
483
484 if (!debugfs_create_file("stats", 0444, stats->directory,
485 (void *)stats, &lpm_stats_fops)) {
486 pr_err("%s: Unable to create %s's overall 'stats' file\n",
487 __func__, stats->name);
488 kfree(stats->time_stats);
489 return -EPERM;
490 }
491
492 return 0;
493}
494
495static ssize_t total_sleep_time_show(struct kobject *kobj,
496 struct kobj_attribute *attr, char *buf)
497{
498 struct lpm_sleep_time *cpu_sleep_time = container_of(attr,
499 struct lpm_sleep_time, ts_attr);
500 unsigned int cpu = cpu_sleep_time->cpu;
501 uint64_t total_time = get_total_sleep_time(cpu);
502
503 return snprintf(buf, MAX_TIME_LEN, "%llu.%09u\n", total_time,
504 do_div(total_time, NSEC_PER_SEC));
505}
506
507static struct kobject *local_module_kobject(void)
508{
509 struct kobject *kobj;
510
511 kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
512
513 if (!kobj) {
514 int err;
515 struct module_kobject *mk;
516
517 mk = kzalloc(sizeof(*mk), GFP_KERNEL);
518 if (!mk)
519 return ERR_PTR(-ENOMEM);
520
521 mk->mod = THIS_MODULE;
522 mk->kobj.kset = module_kset;
523
524 err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL,
525 "%s", KBUILD_MODNAME);
526
527 if (err) {
528 kobject_put(&mk->kobj);
529 kfree(mk);
530 pr_err("%s: cannot create kobject for %s\n",
531 __func__, KBUILD_MODNAME);
532 return ERR_PTR(err);
533 }
534
535 kobject_get(&mk->kobj);
536 kobj = &mk->kobj;
537 }
538
539 return kobj;
540}
541
542static int create_sysfs_node(unsigned int cpu, struct lpm_stats *stats)
543{
544 struct kobject *cpu_kobj = NULL;
545 struct lpm_sleep_time *ts = NULL;
546 struct kobject *stats_kobj;
547 char cpu_name[] = "cpuXX";
548 int ret = -ENOMEM;
549
550 stats_kobj = local_module_kobject();
551
552 if (IS_ERR_OR_NULL(stats_kobj))
553 return PTR_ERR(stats_kobj);
554
555 snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu);
556 cpu_kobj = kobject_create_and_add(cpu_name, stats_kobj);
557 if (!cpu_kobj)
558 return -ENOMEM;
559
560 ts = kzalloc(sizeof(*ts), GFP_KERNEL);
561 if (!ts)
562 goto failed;
563
564 sysfs_attr_init(&ts->ts_attr.attr);
565 ts->ts_attr.attr.name = "total_sleep_time_secs";
566 ts->ts_attr.attr.mode = 0444;
567 ts->ts_attr.show = total_sleep_time_show;
568 ts->ts_attr.store = NULL;
569 ts->cpu = cpu;
570
571 ret = sysfs_create_file(cpu_kobj, &ts->ts_attr.attr);
572 if (ret)
573 goto failed;
574
575 return 0;
576
577failed:
578 kfree(ts);
579 kobject_put(cpu_kobj);
580 return ret;
581}
582
583static struct lpm_stats *config_cpu_level(const char *name,
584 const char **levels, int num_levels, struct lpm_stats *parent,
585 struct cpumask *mask)
586{
587 int cpu = 0;
588 struct lpm_stats *pstats = NULL;
589 struct lpm_stats *stats = NULL;
590
591 for (pstats = parent; pstats; pstats = pstats->parent)
592 cpumask_or(&pstats->mask, &pstats->mask, mask);
593
594 for_each_cpu(cpu, mask) {
595 int ret = 0;
596 char cpu_name[MAX_STR_LEN] = {0};
597
598 stats = &per_cpu(cpu_stats, cpu);
599 snprintf(cpu_name, MAX_STR_LEN, "%s%d", name, cpu);
600 cpumask_set_cpu(cpu, &stats->mask);
601
602 stats->is_cpu = true;
603
604 ret = config_level(cpu_name, levels, num_levels, parent,
605 stats);
606 if (ret) {
607 pr_err("%s: Unable to create %s stats\n",
608 __func__, cpu_name);
609 return ERR_PTR(ret);
610 }
611
612 ret = create_sysfs_node(cpu, stats);
613
614 if (ret) {
615 pr_err("Could not create the sysfs node\n");
616 return ERR_PTR(ret);
617 }
618 }
619
620 return stats;
621}
622
623static void config_suspend_level(struct lpm_stats *stats)
624{
625 suspend_time_stats.name = lpm_stats_suspend;
626 suspend_time_stats.owner = stats;
627 suspend_time_stats.first_bucket_time =
628 CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET;
629 suspend_time_stats.enter_time = 0;
630 suspend_time_stats.success_count = 0;
631 suspend_time_stats.failed_count = 0;
632
633 if (!debugfs_create_file(suspend_time_stats.name, 0444,
634 stats->directory, (void *)&suspend_time_stats,
635 &level_stats_fops))
636 pr_err("%s: Unable to create %s Suspend stats file\n",
637 __func__, stats->name);
638}
639
640static struct lpm_stats *config_cluster_level(const char *name,
641 const char **levels, int num_levels, struct lpm_stats *parent)
642{
643 struct lpm_stats *stats = NULL;
644 int ret = 0;
645
646 stats = kzalloc(sizeof(struct lpm_stats), GFP_KERNEL);
647 if (!stats)
648 return ERR_PTR(-ENOMEM);
649
650 stats->is_cpu = false;
651
652 ret = config_level(name, levels, num_levels, parent, stats);
653 if (ret) {
654 pr_err("%s: Unable to create %s stats\n", __func__,
655 name);
656 kfree(stats);
657 return ERR_PTR(ret);
658 }
659
660 if (!debugfs_create_file("lifo", 0444, stats->directory,
661 (void *)stats, &lifo_stats_fops)) {
662 pr_err("%s: Unable to create %s lifo stats file\n",
663 __func__, stats->name);
664 kfree(stats);
665 return ERR_PTR(-EPERM);
666 }
667
668 if (!parent)
669 config_suspend_level(stats);
670
671 return stats;
672}
673
674static void cleanup_stats(struct lpm_stats *stats)
675{
676 struct list_head *centry = NULL;
677 struct lpm_stats *pos = NULL;
678
679 centry = &stats->child;
680 list_for_each_entry_reverse(pos, centry, sibling) {
681 if (!list_empty(&pos->child))
682 cleanup_stats(pos);
683
684 list_del_init(&pos->child);
685
686 kfree(pos->time_stats);
687 if (!pos->is_cpu)
688 kfree(pos);
689 }
690 kfree(stats->time_stats);
691 kfree(stats);
692}
693
694static void lpm_stats_cleanup(struct lpm_stats *stats)
695{
696 struct lpm_stats *pstats = stats;
697
698 if (!pstats)
699 return;
700
701 while (pstats->parent)
702 pstats = pstats->parent;
703
704 debugfs_remove_recursive(pstats->directory);
705
706 cleanup_stats(pstats);
707}
708
709/**
710 * lpm_stats_config_level() - API to configure levels stats.
711 *
712 * @name: Name of the cluster/cpu.
713 * @levels: Low power mode level names.
714 * @num_levels: Number of leves supported.
715 * @parent: Pointer to the parent's lpm_stats object.
716 * @mask: cpumask, if configuring cpu stats, else NULL.
717 *
718 * Function to communicate the low power mode levels supported by
719 * cpus or a cluster.
720 *
721 * Return: Pointer to the lpm_stats object or ERR_PTR(-ERRNO)
722 */
723struct lpm_stats *lpm_stats_config_level(const char *name,
724 const char **levels, int num_levels, struct lpm_stats *parent,
725 struct cpumask *mask)
726{
727 struct lpm_stats *stats = NULL;
728
729 if (!levels || num_levels <= 0 || IS_ERR(parent)) {
730 pr_err("%s: Invalid input\n\t\tlevels = %p\n\t\t"
731 "num_levels = %d\n\t\tparent = %ld\n",
732 __func__, levels, num_levels, PTR_ERR(parent));
733 return ERR_PTR(-EINVAL);
734 }
735
736 if (mask)
737 stats = config_cpu_level(name, levels, num_levels, parent,
738 mask);
739 else
740 stats = config_cluster_level(name, levels, num_levels,
741 parent);
742
743 if (IS_ERR(stats)) {
744 lpm_stats_cleanup(parent);
745 return stats;
746 }
747
748 return stats;
749}
750EXPORT_SYMBOL(lpm_stats_config_level);
751
752/**
753 * lpm_stats_cluster_enter() - API to communicate the lpm level a cluster
754 * is prepared to enter.
755 *
756 * @stats: Pointer to the cluster's lpm_stats object.
757 * @index: Index of the lpm level that the cluster is going to enter.
758 *
759 * Function to communicate the low power mode level that the cluster is
760 * prepared to enter.
761 */
762void lpm_stats_cluster_enter(struct lpm_stats *stats, uint32_t index)
763{
764 if (IS_ERR_OR_NULL(stats))
765 return;
766
767 update_last_in_stats(stats);
768}
769EXPORT_SYMBOL(lpm_stats_cluster_enter);
770
771/**
772 * lpm_stats_cluster_exit() - API to communicate the lpm level a cluster
773 * exited.
774 *
775 * @stats: Pointer to the cluster's lpm_stats object.
776 * @index: Index of the cluster lpm level.
777 * @success: Success/Failure of the low power mode execution.
778 *
779 * Function to communicate the low power mode level that the cluster
780 * exited.
781 */
782void lpm_stats_cluster_exit(struct lpm_stats *stats, uint32_t index,
783 bool success)
784{
785 if (IS_ERR_OR_NULL(stats))
786 return;
787
788 update_exit_stats(stats, index, success);
789
790 update_first_out_stats(stats);
791}
792EXPORT_SYMBOL(lpm_stats_cluster_exit);
793
794/**
795 * lpm_stats_cpu_enter() - API to communicate the lpm level a cpu
796 * is prepared to enter.
797 *
798 * @index: cpu's lpm level index.
799 *
800 * Function to communicate the low power mode level that the cpu is
801 * prepared to enter.
802 */
803void lpm_stats_cpu_enter(uint32_t index, uint64_t time)
804{
805 struct lpm_stats *stats = &(*this_cpu_ptr(&(cpu_stats)));
806
807 stats->sleep_time = time;
808
809 if (!stats->time_stats)
810 return;
811
812}
813EXPORT_SYMBOL(lpm_stats_cpu_enter);
814
815/**
816 * lpm_stats_cpu_exit() - API to communicate the lpm level that the cpu exited.
817 *
818 * @index: cpu's lpm level index.
819 * @success: Success/Failure of the low power mode execution.
820 *
821 * Function to communicate the low power mode level that the cpu exited.
822 */
823void lpm_stats_cpu_exit(uint32_t index, uint64_t time, bool success)
824{
825 struct lpm_stats *stats = &(*this_cpu_ptr(&(cpu_stats)));
826
827 if (!stats->time_stats)
828 return;
829
830 stats->sleep_time = time - stats->sleep_time;
831
832 update_exit_stats(stats, index, success);
833}
834EXPORT_SYMBOL(lpm_stats_cpu_exit);
835
836/**
837 * lpm_stats_suspend_enter() - API to communicate system entering suspend.
838 *
839 * Function to communicate that the system is ready to enter suspend.
840 */
841void lpm_stats_suspend_enter(void)
842{
843 struct timespec ts;
844
845 getnstimeofday(&ts);
846 suspend_time_stats.enter_time = timespec_to_ns(&ts);
847}
848EXPORT_SYMBOL(lpm_stats_suspend_enter);
849
850/**
851 * lpm_stats_suspend_exit() - API to communicate system exiting suspend.
852 *
853 * Function to communicate that the system exited suspend.
854 */
855void lpm_stats_suspend_exit(void)
856{
857 struct timespec ts;
858 uint64_t exit_time = 0;
859
860 getnstimeofday(&ts);
861 exit_time = timespec_to_ns(&ts) - suspend_time_stats.enter_time;
862 update_level_stats(&suspend_time_stats, exit_time, true);
863}
864EXPORT_SYMBOL(lpm_stats_suspend_exit);