diff options
author | Archana Sathyakumar <asathyak@codeaurora.org> | 2015-11-12 14:58:33 -0700 |
---|---|---|
committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 21:18:05 -0700 |
commit | 40fe32a315bc3a1b2484ad49ccc602325b0c782a (patch) | |
tree | 2f78065ce9e3d67dc8bea26584c27a5a96b594ea /drivers/power/qcom | |
parent | 277fb0f7d04a9f8005aecce1682a8142d8d18362 (diff) |
lpm: Add sysfs node to display total sleep time
Add sysfs node to query the total sleep time of a cpu since debugfs node
is not exposed to the userspace service.
Command:
cat /sys/module/lpm_stats/cpu0/total_sleep_time_secs
cat /sys/module/lpm_stats/cpu1/total_sleep_time_secs
cat /sys/module/lpm_stats/cpu2/total_sleep_time_secs
cat /sys/module/lpm_stats/cpu3/total_sleep_time_secs
CRs-fixed: 935207
Change-Id: I45be0a8be29932816aa42e097657a2a60933b986
Signed-off-by: Archana Sathyakumar <asathyak@codeaurora.org>
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
Diffstat (limited to 'drivers/power/qcom')
-rw-r--r-- | drivers/power/qcom/lpm-stats.c | 115 |
1 files changed, 114 insertions, 1 deletions
diff --git a/drivers/power/qcom/lpm-stats.c b/drivers/power/qcom/lpm-stats.c index 1a311d9214ab..3b855ea33b50 100644 --- a/drivers/power/qcom/lpm-stats.c +++ b/drivers/power/qcom/lpm-stats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -26,9 +26,15 @@ #include <soc/qcom/pm.h> #define MAX_STR_LEN 256 +#define MAX_TIME_LEN 20 const char *lpm_stats_reset = "reset"; const char *lpm_stats_suspend = "suspend"; +struct lpm_sleep_time { + struct kobj_attribute ts_attr; + unsigned int cpu; +}; + struct level_stats { const char *name; struct lpm_stats *owner; @@ -64,6 +70,18 @@ static struct level_stats suspend_time_stats; static DEFINE_PER_CPU_SHARED_ALIGNED(struct lpm_stats, cpu_stats); +static uint64_t get_total_sleep_time(unsigned int cpu_id) +{ + struct lpm_stats *stats = &per_cpu(cpu_stats, cpu_id); + int i; + uint64_t ret = 0; + + for (i = 0; i < stats->num_levels; i++) + ret += stats->time_stats[i].total_time; + + return ret; +} + static void update_level_stats(struct level_stats *stats, uint64_t t, bool success) { @@ -499,6 +517,94 @@ static int config_level(const char *name, const char **levels, return 0; } +static ssize_t total_sleep_time_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct lpm_sleep_time *cpu_sleep_time = container_of(attr, + struct lpm_sleep_time, ts_attr); + unsigned int cpu = cpu_sleep_time->cpu; + uint64_t total_time = get_total_sleep_time(cpu); + + return snprintf(buf, MAX_TIME_LEN, "%llu.%09u\n", total_time, + do_div(total_time, NSEC_PER_SEC)); +} + +static struct kobject *local_module_kobject(void) +{ + struct kobject *kobj; + + kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + + if (!kobj) { + int err; + struct module_kobject *mk; + + mk = kzalloc(sizeof(*mk), GFP_KERNEL); + if (!mk) + return ERR_PTR(-ENOMEM); + + mk->mod = THIS_MODULE; + mk->kobj.kset = module_kset; + + err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL, + "%s", KBUILD_MODNAME); + + if (err) { + kobject_put(&mk->kobj); + kfree(mk); + pr_err("%s: cannot create kobject for %s\n", + __func__, KBUILD_MODNAME); + return ERR_PTR(err); + } + + kobject_get(&mk->kobj); + kobj = &mk->kobj; + } + + return kobj; +} + +static int create_sysfs_node(unsigned int cpu, struct lpm_stats *stats) +{ + struct kobject *cpu_kobj = NULL; + struct lpm_sleep_time *ts = NULL; + struct kobject *stats_kobj; + char cpu_name[] = "cpuXX"; + int ret = -ENOMEM; + + stats_kobj = local_module_kobject(); + + if (IS_ERR_OR_NULL(stats_kobj)) + return PTR_ERR(stats_kobj); + + snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu); + cpu_kobj = kobject_create_and_add(cpu_name, stats_kobj); + if (!cpu_kobj) + return -ENOMEM; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (!ts) + goto failed; + + sysfs_attr_init(&ts->ts_attr.attr); + ts->ts_attr.attr.name = "total_sleep_time_secs"; + ts->ts_attr.attr.mode = 0444; + ts->ts_attr.show = total_sleep_time_show; + ts->ts_attr.store = NULL; + ts->cpu = cpu; + + ret = sysfs_create_file(cpu_kobj, &ts->ts_attr.attr); + if (ret) + goto failed; + + return 0; + +failed: + kfree(ts); + kobject_put(cpu_kobj); + return ret; +} + static struct lpm_stats *config_cpu_level(const char *name, const char **levels, int num_levels, struct lpm_stats *parent, struct cpumask *mask) @@ -527,6 +633,13 @@ static struct lpm_stats *config_cpu_level(const char *name, __func__, cpu_name); return ERR_PTR(ret); } + + ret = create_sysfs_node(cpu, stats); + + if (ret) { + pr_err("Could not create the sysfs node\n"); + return ERR_PTR(ret); + } } return stats; |