1 // SPDX-License-Identifier: GPL-2.0
3 * Wakeup statistics in sysfs
5 * Copyright (c) 2019 Linux Foundation
6 * Copyright (c) 2019 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
7 * Copyright (c) 2019 Google Inc.
10 #include <linux/device.h>
11 #include <linux/idr.h>
12 #include <linux/init.h>
13 #include <linux/kdev_t.h>
14 #include <linux/kernel.h>
15 #include <linux/kobject.h>
16 #include <linux/slab.h>
17 #include <linux/timekeeping.h>
21 static struct class *wakeup_class
;
23 #define wakeup_attr(_name) \
24 static ssize_t _name##_show(struct device *dev, \
25 struct device_attribute *attr, char *buf) \
27 struct wakeup_source *ws = dev_get_drvdata(dev); \
29 return sysfs_emit(buf, "%lu\n", ws->_name); \
31 static DEVICE_ATTR_RO(_name)
33 wakeup_attr(active_count
);
34 wakeup_attr(event_count
);
35 wakeup_attr(wakeup_count
);
36 wakeup_attr(expire_count
);
38 static ssize_t
active_time_ms_show(struct device
*dev
,
39 struct device_attribute
*attr
, char *buf
)
41 struct wakeup_source
*ws
= dev_get_drvdata(dev
);
43 ws
->active
? ktime_sub(ktime_get(), ws
->last_time
) : 0;
45 return sysfs_emit(buf
, "%lld\n", ktime_to_ms(active_time
));
47 static DEVICE_ATTR_RO(active_time_ms
);
49 static ssize_t
total_time_ms_show(struct device
*dev
,
50 struct device_attribute
*attr
, char *buf
)
52 struct wakeup_source
*ws
= dev_get_drvdata(dev
);
54 ktime_t total_time
= ws
->total_time
;
57 active_time
= ktime_sub(ktime_get(), ws
->last_time
);
58 total_time
= ktime_add(total_time
, active_time
);
61 return sysfs_emit(buf
, "%lld\n", ktime_to_ms(total_time
));
63 static DEVICE_ATTR_RO(total_time_ms
);
65 static ssize_t
max_time_ms_show(struct device
*dev
,
66 struct device_attribute
*attr
, char *buf
)
68 struct wakeup_source
*ws
= dev_get_drvdata(dev
);
70 ktime_t max_time
= ws
->max_time
;
73 active_time
= ktime_sub(ktime_get(), ws
->last_time
);
74 if (active_time
> max_time
)
75 max_time
= active_time
;
78 return sysfs_emit(buf
, "%lld\n", ktime_to_ms(max_time
));
80 static DEVICE_ATTR_RO(max_time_ms
);
82 static ssize_t
last_change_ms_show(struct device
*dev
,
83 struct device_attribute
*attr
, char *buf
)
85 struct wakeup_source
*ws
= dev_get_drvdata(dev
);
87 return sysfs_emit(buf
, "%lld\n", ktime_to_ms(ws
->last_time
));
89 static DEVICE_ATTR_RO(last_change_ms
);
91 static ssize_t
name_show(struct device
*dev
, struct device_attribute
*attr
,
94 struct wakeup_source
*ws
= dev_get_drvdata(dev
);
96 return sysfs_emit(buf
, "%s\n", ws
->name
);
98 static DEVICE_ATTR_RO(name
);
100 static ssize_t
prevent_suspend_time_ms_show(struct device
*dev
,
101 struct device_attribute
*attr
,
104 struct wakeup_source
*ws
= dev_get_drvdata(dev
);
105 ktime_t prevent_sleep_time
= ws
->prevent_sleep_time
;
107 if (ws
->active
&& ws
->autosleep_enabled
) {
108 prevent_sleep_time
= ktime_add(prevent_sleep_time
,
109 ktime_sub(ktime_get(), ws
->start_prevent_time
));
112 return sysfs_emit(buf
, "%lld\n", ktime_to_ms(prevent_sleep_time
));
114 static DEVICE_ATTR_RO(prevent_suspend_time_ms
);
116 static struct attribute
*wakeup_source_attrs
[] = {
118 &dev_attr_active_count
.attr
,
119 &dev_attr_event_count
.attr
,
120 &dev_attr_wakeup_count
.attr
,
121 &dev_attr_expire_count
.attr
,
122 &dev_attr_active_time_ms
.attr
,
123 &dev_attr_total_time_ms
.attr
,
124 &dev_attr_max_time_ms
.attr
,
125 &dev_attr_last_change_ms
.attr
,
126 &dev_attr_prevent_suspend_time_ms
.attr
,
129 ATTRIBUTE_GROUPS(wakeup_source
);
131 static void device_create_release(struct device
*dev
)
136 static struct device
*wakeup_source_device_create(struct device
*parent
,
137 struct wakeup_source
*ws
)
139 struct device
*dev
= NULL
;
140 int retval
= -ENODEV
;
142 dev
= kzalloc(sizeof(*dev
), GFP_KERNEL
);
148 device_initialize(dev
);
149 dev
->devt
= MKDEV(0, 0);
150 dev
->class = wakeup_class
;
151 dev
->parent
= parent
;
152 dev
->groups
= wakeup_source_groups
;
153 dev
->release
= device_create_release
;
154 dev_set_drvdata(dev
, ws
);
155 device_set_pm_not_required(dev
);
157 retval
= kobject_set_name(&dev
->kobj
, "wakeup%d", ws
->id
);
161 retval
= device_add(dev
);
169 return ERR_PTR(retval
);
173 * wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs.
174 * @parent: Device given wakeup source is associated with (or NULL if virtual).
175 * @ws: Wakeup source to be added in sysfs.
177 int wakeup_source_sysfs_add(struct device
*parent
, struct wakeup_source
*ws
)
181 dev
= wakeup_source_device_create(parent
, ws
);
190 * pm_wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs
191 * for a device if they're missing.
192 * @parent: Device given wakeup source is associated with
194 int pm_wakeup_source_sysfs_add(struct device
*parent
)
196 if (!parent
->power
.wakeup
|| parent
->power
.wakeup
->dev
)
199 return wakeup_source_sysfs_add(parent
, parent
->power
.wakeup
);
203 * wakeup_source_sysfs_remove - Remove wakeup_source attributes from sysfs.
204 * @ws: Wakeup source to be removed from sysfs.
206 void wakeup_source_sysfs_remove(struct wakeup_source
*ws
)
208 device_unregister(ws
->dev
);
211 static int __init
wakeup_sources_sysfs_init(void)
213 wakeup_class
= class_create(THIS_MODULE
, "wakeup");
215 return PTR_ERR_OR_ZERO(wakeup_class
);
217 postcore_initcall(wakeup_sources_sysfs_init
);