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