xref: /openbmc/linux/drivers/base/power/wakeup_stats.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1c8377adfSTri Vo // SPDX-License-Identifier: GPL-2.0
2c8377adfSTri Vo /*
3c8377adfSTri Vo  * Wakeup statistics in sysfs
4c8377adfSTri Vo  *
5c8377adfSTri Vo  * Copyright (c) 2019 Linux Foundation
6c8377adfSTri Vo  * Copyright (c) 2019 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
7c8377adfSTri Vo  * Copyright (c) 2019 Google Inc.
8c8377adfSTri Vo  */
9c8377adfSTri Vo 
10c8377adfSTri Vo #include <linux/device.h>
11c8377adfSTri Vo #include <linux/idr.h>
12c8377adfSTri Vo #include <linux/init.h>
13c8377adfSTri Vo #include <linux/kdev_t.h>
14c8377adfSTri Vo #include <linux/kernel.h>
15c8377adfSTri Vo #include <linux/kobject.h>
16c8377adfSTri Vo #include <linux/slab.h>
17c8377adfSTri Vo #include <linux/timekeeping.h>
18c8377adfSTri Vo 
19c8377adfSTri Vo #include "power.h"
20c8377adfSTri Vo 
21c8377adfSTri Vo static struct class *wakeup_class;
22c8377adfSTri Vo 
23c8377adfSTri Vo #define wakeup_attr(_name)						\
24c8377adfSTri Vo static ssize_t _name##_show(struct device *dev,				\
25c8377adfSTri Vo 			    struct device_attribute *attr, char *buf)	\
26c8377adfSTri Vo {									\
27c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);		\
28c8377adfSTri Vo 									\
29948b3edbSJoe Perches 	return sysfs_emit(buf, "%lu\n", ws->_name);			\
30c8377adfSTri Vo }									\
31c8377adfSTri Vo static DEVICE_ATTR_RO(_name)
32c8377adfSTri Vo 
33c8377adfSTri Vo wakeup_attr(active_count);
34c8377adfSTri Vo wakeup_attr(event_count);
35c8377adfSTri Vo wakeup_attr(wakeup_count);
36c8377adfSTri Vo wakeup_attr(expire_count);
37c8377adfSTri Vo 
active_time_ms_show(struct device * dev,struct device_attribute * attr,char * buf)38c8377adfSTri Vo static ssize_t active_time_ms_show(struct device *dev,
39c8377adfSTri Vo 				   struct device_attribute *attr, char *buf)
40c8377adfSTri Vo {
41c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);
42c8377adfSTri Vo 	ktime_t active_time =
43c8377adfSTri Vo 		ws->active ? ktime_sub(ktime_get(), ws->last_time) : 0;
44c8377adfSTri Vo 
45aa838896SJoe Perches 	return sysfs_emit(buf, "%lld\n", ktime_to_ms(active_time));
46c8377adfSTri Vo }
47c8377adfSTri Vo static DEVICE_ATTR_RO(active_time_ms);
48c8377adfSTri Vo 
total_time_ms_show(struct device * dev,struct device_attribute * attr,char * buf)49c8377adfSTri Vo static ssize_t total_time_ms_show(struct device *dev,
50c8377adfSTri Vo 				  struct device_attribute *attr, char *buf)
51c8377adfSTri Vo {
52c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);
53c8377adfSTri Vo 	ktime_t active_time;
54c8377adfSTri Vo 	ktime_t total_time = ws->total_time;
55c8377adfSTri Vo 
56c8377adfSTri Vo 	if (ws->active) {
57c8377adfSTri Vo 		active_time = ktime_sub(ktime_get(), ws->last_time);
58c8377adfSTri Vo 		total_time = ktime_add(total_time, active_time);
59c8377adfSTri Vo 	}
60948b3edbSJoe Perches 
61aa838896SJoe Perches 	return sysfs_emit(buf, "%lld\n", ktime_to_ms(total_time));
62c8377adfSTri Vo }
63c8377adfSTri Vo static DEVICE_ATTR_RO(total_time_ms);
64c8377adfSTri Vo 
max_time_ms_show(struct device * dev,struct device_attribute * attr,char * buf)65c8377adfSTri Vo static ssize_t max_time_ms_show(struct device *dev,
66c8377adfSTri Vo 				struct device_attribute *attr, char *buf)
67c8377adfSTri Vo {
68c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);
69c8377adfSTri Vo 	ktime_t active_time;
70c8377adfSTri Vo 	ktime_t max_time = ws->max_time;
71c8377adfSTri Vo 
72c8377adfSTri Vo 	if (ws->active) {
73c8377adfSTri Vo 		active_time = ktime_sub(ktime_get(), ws->last_time);
74c8377adfSTri Vo 		if (active_time > max_time)
75c8377adfSTri Vo 			max_time = active_time;
76c8377adfSTri Vo 	}
77948b3edbSJoe Perches 
78aa838896SJoe Perches 	return sysfs_emit(buf, "%lld\n", ktime_to_ms(max_time));
79c8377adfSTri Vo }
80c8377adfSTri Vo static DEVICE_ATTR_RO(max_time_ms);
81c8377adfSTri Vo 
last_change_ms_show(struct device * dev,struct device_attribute * attr,char * buf)82c8377adfSTri Vo static ssize_t last_change_ms_show(struct device *dev,
83c8377adfSTri Vo 				   struct device_attribute *attr, char *buf)
84c8377adfSTri Vo {
85c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);
86c8377adfSTri Vo 
87aa838896SJoe Perches 	return sysfs_emit(buf, "%lld\n", ktime_to_ms(ws->last_time));
88c8377adfSTri Vo }
89c8377adfSTri Vo static DEVICE_ATTR_RO(last_change_ms);
90c8377adfSTri Vo 
name_show(struct device * dev,struct device_attribute * attr,char * buf)91c8377adfSTri Vo static ssize_t name_show(struct device *dev, struct device_attribute *attr,
92c8377adfSTri Vo 			 char *buf)
93c8377adfSTri Vo {
94c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);
95c8377adfSTri Vo 
96aa838896SJoe Perches 	return sysfs_emit(buf, "%s\n", ws->name);
97c8377adfSTri Vo }
98c8377adfSTri Vo static DEVICE_ATTR_RO(name);
99c8377adfSTri Vo 
prevent_suspend_time_ms_show(struct device * dev,struct device_attribute * attr,char * buf)100c8377adfSTri Vo static ssize_t prevent_suspend_time_ms_show(struct device *dev,
101c8377adfSTri Vo 					    struct device_attribute *attr,
102c8377adfSTri Vo 					    char *buf)
103c8377adfSTri Vo {
104c8377adfSTri Vo 	struct wakeup_source *ws = dev_get_drvdata(dev);
105c8377adfSTri Vo 	ktime_t prevent_sleep_time = ws->prevent_sleep_time;
106c8377adfSTri Vo 
107c8377adfSTri Vo 	if (ws->active && ws->autosleep_enabled) {
108c8377adfSTri Vo 		prevent_sleep_time = ktime_add(prevent_sleep_time,
109c8377adfSTri Vo 			ktime_sub(ktime_get(), ws->start_prevent_time));
110c8377adfSTri Vo 	}
111948b3edbSJoe Perches 
112aa838896SJoe Perches 	return sysfs_emit(buf, "%lld\n", ktime_to_ms(prevent_sleep_time));
113c8377adfSTri Vo }
114c8377adfSTri Vo static DEVICE_ATTR_RO(prevent_suspend_time_ms);
115c8377adfSTri Vo 
116c8377adfSTri Vo static struct attribute *wakeup_source_attrs[] = {
117c8377adfSTri Vo 	&dev_attr_name.attr,
118c8377adfSTri Vo 	&dev_attr_active_count.attr,
119c8377adfSTri Vo 	&dev_attr_event_count.attr,
120c8377adfSTri Vo 	&dev_attr_wakeup_count.attr,
121c8377adfSTri Vo 	&dev_attr_expire_count.attr,
122c8377adfSTri Vo 	&dev_attr_active_time_ms.attr,
123c8377adfSTri Vo 	&dev_attr_total_time_ms.attr,
124c8377adfSTri Vo 	&dev_attr_max_time_ms.attr,
125c8377adfSTri Vo 	&dev_attr_last_change_ms.attr,
126c8377adfSTri Vo 	&dev_attr_prevent_suspend_time_ms.attr,
127c8377adfSTri Vo 	NULL,
128c8377adfSTri Vo };
129c8377adfSTri Vo ATTRIBUTE_GROUPS(wakeup_source);
130c8377adfSTri Vo 
device_create_release(struct device * dev)131c8377adfSTri Vo static void device_create_release(struct device *dev)
132c8377adfSTri Vo {
133c8377adfSTri Vo 	kfree(dev);
134c8377adfSTri Vo }
135c8377adfSTri Vo 
wakeup_source_device_create(struct device * parent,struct wakeup_source * ws)136c8377adfSTri Vo static struct device *wakeup_source_device_create(struct device *parent,
137c8377adfSTri Vo 						  struct wakeup_source *ws)
138c8377adfSTri Vo {
139c8377adfSTri Vo 	struct device *dev = NULL;
140e4880233SColin Ian King 	int retval;
141c8377adfSTri Vo 
142c8377adfSTri Vo 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
143c8377adfSTri Vo 	if (!dev) {
144c8377adfSTri Vo 		retval = -ENOMEM;
145c8377adfSTri Vo 		goto error;
146c8377adfSTri Vo 	}
147c8377adfSTri Vo 
148c8377adfSTri Vo 	device_initialize(dev);
149c8377adfSTri Vo 	dev->devt = MKDEV(0, 0);
150c8377adfSTri Vo 	dev->class = wakeup_class;
151c8377adfSTri Vo 	dev->parent = parent;
152c8377adfSTri Vo 	dev->groups = wakeup_source_groups;
153c8377adfSTri Vo 	dev->release = device_create_release;
154c8377adfSTri Vo 	dev_set_drvdata(dev, ws);
155c8377adfSTri Vo 	device_set_pm_not_required(dev);
156c8377adfSTri Vo 
1574da6d76fSAndy Shevchenko 	retval = dev_set_name(dev, "wakeup%d", ws->id);
158c8377adfSTri Vo 	if (retval)
159c8377adfSTri Vo 		goto error;
160c8377adfSTri Vo 
161c8377adfSTri Vo 	retval = device_add(dev);
162c8377adfSTri Vo 	if (retval)
163c8377adfSTri Vo 		goto error;
164c8377adfSTri Vo 
165c8377adfSTri Vo 	return dev;
166c8377adfSTri Vo 
167c8377adfSTri Vo error:
168c8377adfSTri Vo 	put_device(dev);
169c8377adfSTri Vo 	return ERR_PTR(retval);
170c8377adfSTri Vo }
171c8377adfSTri Vo 
172c8377adfSTri Vo /**
173c8377adfSTri Vo  * wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs.
174c8377adfSTri Vo  * @parent: Device given wakeup source is associated with (or NULL if virtual).
175c8377adfSTri Vo  * @ws: Wakeup source to be added in sysfs.
176c8377adfSTri Vo  */
wakeup_source_sysfs_add(struct device * parent,struct wakeup_source * ws)177c8377adfSTri Vo int wakeup_source_sysfs_add(struct device *parent, struct wakeup_source *ws)
178c8377adfSTri Vo {
179c8377adfSTri Vo 	struct device *dev;
180c8377adfSTri Vo 
181c8377adfSTri Vo 	dev = wakeup_source_device_create(parent, ws);
182c8377adfSTri Vo 	if (IS_ERR(dev))
183c8377adfSTri Vo 		return PTR_ERR(dev);
184c8377adfSTri Vo 	ws->dev = dev;
185c8377adfSTri Vo 
186c8377adfSTri Vo 	return 0;
187c8377adfSTri Vo }
188c8377adfSTri Vo 
189c8377adfSTri Vo /**
1902ca3d1ecSStephen Boyd  * pm_wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs
1912ca3d1ecSStephen Boyd  * for a device if they're missing.
1922ca3d1ecSStephen Boyd  * @parent: Device given wakeup source is associated with
1932ca3d1ecSStephen Boyd  */
pm_wakeup_source_sysfs_add(struct device * parent)1942ca3d1ecSStephen Boyd int pm_wakeup_source_sysfs_add(struct device *parent)
1952ca3d1ecSStephen Boyd {
1962ca3d1ecSStephen Boyd 	if (!parent->power.wakeup || parent->power.wakeup->dev)
1972ca3d1ecSStephen Boyd 		return 0;
1982ca3d1ecSStephen Boyd 
1992ca3d1ecSStephen Boyd 	return wakeup_source_sysfs_add(parent, parent->power.wakeup);
2002ca3d1ecSStephen Boyd }
2012ca3d1ecSStephen Boyd 
2022ca3d1ecSStephen Boyd /**
203c8377adfSTri Vo  * wakeup_source_sysfs_remove - Remove wakeup_source attributes from sysfs.
204c8377adfSTri Vo  * @ws: Wakeup source to be removed from sysfs.
205c8377adfSTri Vo  */
wakeup_source_sysfs_remove(struct wakeup_source * ws)206c8377adfSTri Vo void wakeup_source_sysfs_remove(struct wakeup_source *ws)
207c8377adfSTri Vo {
208c8377adfSTri Vo 	device_unregister(ws->dev);
209c8377adfSTri Vo }
210c8377adfSTri Vo 
wakeup_sources_sysfs_init(void)211c8377adfSTri Vo static int __init wakeup_sources_sysfs_init(void)
212c8377adfSTri Vo {
213*1aaba11dSGreg Kroah-Hartman 	wakeup_class = class_create("wakeup");
214c8377adfSTri Vo 
215c8377adfSTri Vo 	return PTR_ERR_OR_ZERO(wakeup_class);
216c8377adfSTri Vo }
217c8377adfSTri Vo postcore_initcall(wakeup_sources_sysfs_init);
218