summaryrefslogtreecommitdiff
path: root/drivers/base/power/sysfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/sysfs.c')
-rw-r--r--drivers/base/power/sysfs.c270
1 files changed, 185 insertions, 85 deletions
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index d713738ce796..13b31a3adc77 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -1,8 +1,7 @@
-/*
- * drivers/base/power/sysfs.c - sysfs entries for device PM
- */
-
+// SPDX-License-Identifier: GPL-2.0
+/* sysfs entries for device PM */
#include <linux/device.h>
+#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/export.h>
#include <linux/pm_qos.h>
@@ -101,8 +100,8 @@ static const char ctrl_on[] = "on";
static ssize_t control_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%s\n",
- dev->power.runtime_auto ? ctrl_auto : ctrl_on);
+ return sysfs_emit(buf, "%s\n",
+ dev->power.runtime_auto ? ctrl_auto : ctrl_on);
}
static ssize_t control_store(struct device * dev, struct device_attribute *attr,
@@ -122,70 +121,71 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(control);
static ssize_t runtime_active_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
- int ret;
- spin_lock_irq(&dev->power.lock);
- update_pm_runtime_accounting(dev);
- ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies));
- spin_unlock_irq(&dev->power.lock);
- return ret;
+ u64 tmp = pm_runtime_active_time(dev);
+
+ do_div(tmp, NSEC_PER_MSEC);
+
+ return sysfs_emit(buf, "%llu\n", tmp);
}
static DEVICE_ATTR_RO(runtime_active_time);
static ssize_t runtime_suspended_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
- int ret;
- spin_lock_irq(&dev->power.lock);
- update_pm_runtime_accounting(dev);
- ret = sprintf(buf, "%i\n",
- jiffies_to_msecs(dev->power.suspended_jiffies));
- spin_unlock_irq(&dev->power.lock);
- return ret;
+ u64 tmp = pm_runtime_suspended_time(dev);
+
+ do_div(tmp, NSEC_PER_MSEC);
+
+ return sysfs_emit(buf, "%llu\n", tmp);
}
static DEVICE_ATTR_RO(runtime_suspended_time);
static ssize_t runtime_status_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
- const char *p;
+ const char *output;
if (dev->power.runtime_error) {
- p = "error\n";
+ output = "error";
} else if (dev->power.disable_depth) {
- p = "unsupported\n";
+ output = "unsupported";
} else {
switch (dev->power.runtime_status) {
case RPM_SUSPENDED:
- p = "suspended\n";
+ output = "suspended";
break;
case RPM_SUSPENDING:
- p = "suspending\n";
+ output = "suspending";
break;
case RPM_RESUMING:
- p = "resuming\n";
+ output = "resuming";
break;
case RPM_ACTIVE:
- p = "active\n";
+ output = "active";
break;
default:
return -EIO;
}
}
- return sprintf(buf, p);
+ return sysfs_emit(buf, "%s\n", output);
}
static DEVICE_ATTR_RO(runtime_status);
static ssize_t autosuspend_delay_ms_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
if (!dev->power.use_autosuspend)
return -EIO;
- return sprintf(buf, "%d\n", dev->power.autosuspend_delay);
+
+ return sysfs_emit(buf, "%d\n", dev->power.autosuspend_delay);
}
static ssize_t autosuspend_delay_ms_store(struct device *dev,
@@ -214,11 +214,11 @@ static ssize_t pm_qos_resume_latency_us_show(struct device *dev,
s32 value = dev_pm_qos_requested_resume_latency(dev);
if (value == 0)
- return sprintf(buf, "n/a\n");
+ return sysfs_emit(buf, "n/a\n");
if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
value = 0;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t pm_qos_resume_latency_us_store(struct device *dev,
@@ -258,11 +258,11 @@ static ssize_t pm_qos_latency_tolerance_us_show(struct device *dev,
s32 value = dev_pm_qos_get_user_latency_tolerance(dev);
if (value < 0)
- return sprintf(buf, "auto\n");
+ return sysfs_emit(buf, "%s\n", "auto");
if (value == PM_QOS_LATENCY_ANY)
- return sprintf(buf, "any\n");
+ return sysfs_emit(buf, "%s\n", "any");
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t pm_qos_latency_tolerance_us_store(struct device *dev,
@@ -294,8 +294,8 @@ static ssize_t pm_qos_no_power_off_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev)
- & PM_QOS_FLAG_NO_POWER_OFF));
+ return sysfs_emit(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev)
+ & PM_QOS_FLAG_NO_POWER_OFF));
}
static ssize_t pm_qos_no_power_off_store(struct device *dev,
@@ -323,9 +323,9 @@ static const char _disabled[] = "disabled";
static ssize_t wakeup_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%s\n", device_can_wakeup(dev)
- ? (device_may_wakeup(dev) ? _enabled : _disabled)
- : "");
+ return sysfs_emit(buf, "%s\n", device_can_wakeup(dev)
+ ? (device_may_wakeup(dev) ? _enabled : _disabled)
+ : "");
}
static ssize_t wakeup_store(struct device *dev, struct device_attribute *attr,
@@ -348,7 +348,7 @@ static DEVICE_ATTR_RW(wakeup);
static ssize_t wakeup_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- unsigned long count = 0;
+ unsigned long count;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -357,7 +357,10 @@ static ssize_t wakeup_count_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lu\n", count);
}
static DEVICE_ATTR_RO(wakeup_count);
@@ -366,7 +369,7 @@ static ssize_t wakeup_active_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- unsigned long count = 0;
+ unsigned long count;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -375,7 +378,10 @@ static ssize_t wakeup_active_count_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lu\n", count);
}
static DEVICE_ATTR_RO(wakeup_active_count);
@@ -384,7 +390,7 @@ static ssize_t wakeup_abort_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- unsigned long count = 0;
+ unsigned long count;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -393,7 +399,10 @@ static ssize_t wakeup_abort_count_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lu\n", count);
}
static DEVICE_ATTR_RO(wakeup_abort_count);
@@ -402,7 +411,7 @@ static ssize_t wakeup_expire_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- unsigned long count = 0;
+ unsigned long count;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -411,7 +420,10 @@ static ssize_t wakeup_expire_count_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lu\n", count);
}
static DEVICE_ATTR_RO(wakeup_expire_count);
@@ -419,7 +431,7 @@ static DEVICE_ATTR_RO(wakeup_expire_count);
static ssize_t wakeup_active_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- unsigned int active = 0;
+ unsigned int active;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -428,7 +440,10 @@ static ssize_t wakeup_active_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%u\n", active) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%u\n", active);
}
static DEVICE_ATTR_RO(wakeup_active);
@@ -437,7 +452,7 @@ static ssize_t wakeup_total_time_ms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- s64 msec = 0;
+ s64 msec;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -446,7 +461,10 @@ static ssize_t wakeup_total_time_ms_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lld\n", msec);
}
static DEVICE_ATTR_RO(wakeup_total_time_ms);
@@ -454,7 +472,7 @@ static DEVICE_ATTR_RO(wakeup_total_time_ms);
static ssize_t wakeup_max_time_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- s64 msec = 0;
+ s64 msec;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -463,7 +481,10 @@ static ssize_t wakeup_max_time_ms_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lld\n", msec);
}
static DEVICE_ATTR_RO(wakeup_max_time_ms);
@@ -472,7 +493,7 @@ static ssize_t wakeup_last_time_ms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- s64 msec = 0;
+ s64 msec;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -481,7 +502,10 @@ static ssize_t wakeup_last_time_ms_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lld\n", msec);
}
static DEVICE_ATTR_RO(wakeup_last_time_ms);
@@ -491,7 +515,7 @@ static ssize_t wakeup_prevent_sleep_time_ms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- s64 msec = 0;
+ s64 msec;
bool enabled = false;
spin_lock_irq(&dev->power.lock);
@@ -500,18 +524,36 @@ static ssize_t wakeup_prevent_sleep_time_ms_show(struct device *dev,
enabled = true;
}
spin_unlock_irq(&dev->power.lock);
- return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
+
+ if (!enabled)
+ return sysfs_emit(buf, "\n");
+ return sysfs_emit(buf, "%lld\n", msec);
}
static DEVICE_ATTR_RO(wakeup_prevent_sleep_time_ms);
#endif /* CONFIG_PM_AUTOSLEEP */
-#endif /* CONFIG_PM_SLEEP */
+
+static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid,
+ kgid_t kgid)
+{
+ if (dev->power.wakeup && dev->power.wakeup->dev)
+ return device_change_owner(dev->power.wakeup->dev, kuid, kgid);
+ return 0;
+}
+
+#else /* CONFIG_PM_SLEEP */
+static inline int dpm_sysfs_wakeup_change_owner(struct device *dev, kuid_t kuid,
+ kgid_t kgid)
+{
+ return 0;
+}
+#endif
#ifdef CONFIG_PM_ADVANCED_DEBUG
static ssize_t runtime_usage_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%d\n", atomic_read(&dev->power.usage_count));
+ return sysfs_emit(buf, "%d\n", atomic_read(&dev->power.usage_count));
}
static DEVICE_ATTR_RO(runtime_usage);
@@ -519,21 +561,26 @@ static ssize_t runtime_active_kids_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%d\n", dev->power.ignore_children ?
- 0 : atomic_read(&dev->power.child_count));
+ return sysfs_emit(buf, "%d\n", dev->power.ignore_children ?
+ 0 : atomic_read(&dev->power.child_count));
}
static DEVICE_ATTR_RO(runtime_active_kids);
static ssize_t runtime_enabled_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- if (dev->power.disable_depth && (dev->power.runtime_auto == false))
- return sprintf(buf, "disabled & forbidden\n");
- if (dev->power.disable_depth)
- return sprintf(buf, "disabled\n");
- if (dev->power.runtime_auto == false)
- return sprintf(buf, "forbidden\n");
- return sprintf(buf, "enabled\n");
+ const char *output;
+
+ if (dev->power.disable_depth && !dev->power.runtime_auto)
+ output = "disabled & forbidden";
+ else if (dev->power.disable_depth)
+ output = "disabled";
+ else if (!dev->power.runtime_auto)
+ output = "forbidden";
+ else
+ output = "enabled";
+
+ return sysfs_emit(buf, "%s\n", output);
}
static DEVICE_ATTR_RO(runtime_enabled);
@@ -541,9 +588,9 @@ static DEVICE_ATTR_RO(runtime_enabled);
static ssize_t async_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%s\n",
- device_async_suspend_enabled(dev) ?
- _enabled : _disabled);
+ return sysfs_emit(buf, "%s\n",
+ device_async_suspend_enabled(dev) ?
+ _enabled : _disabled);
}
static ssize_t async_store(struct device *dev, struct device_attribute *attr,
@@ -564,15 +611,9 @@ static DEVICE_ATTR_RW(async);
#endif /* CONFIG_PM_ADVANCED_DEBUG */
static struct attribute *power_attrs[] = {
-#ifdef CONFIG_PM_ADVANCED_DEBUG
-#ifdef CONFIG_PM_SLEEP
+#if defined(CONFIG_PM_ADVANCED_DEBUG) && defined(CONFIG_PM_SLEEP)
&dev_attr_async.attr,
#endif
- &dev_attr_runtime_status.attr,
- &dev_attr_runtime_usage.attr,
- &dev_attr_runtime_active_kids.attr,
- &dev_attr_runtime_enabled.attr,
-#endif /* CONFIG_PM_ADVANCED_DEBUG */
NULL,
};
static const struct attribute_group pm_attr_group = {
@@ -603,13 +644,16 @@ static const struct attribute_group pm_wakeup_attr_group = {
};
static struct attribute *runtime_attrs[] = {
-#ifndef CONFIG_PM_ADVANCED_DEBUG
&dev_attr_runtime_status.attr,
-#endif
&dev_attr_control.attr,
&dev_attr_runtime_suspended_time.attr,
&dev_attr_runtime_active_time.attr,
&dev_attr_autosuspend_delay_ms.attr,
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ &dev_attr_runtime_usage.attr,
+ &dev_attr_runtime_active_kids.attr,
+ &dev_attr_runtime_enabled.attr,
+#endif
NULL,
};
static const struct attribute_group pm_runtime_attr_group = {
@@ -648,11 +692,15 @@ int dpm_sysfs_add(struct device *dev)
{
int rc;
+ /* No need to create PM sysfs if explicitly disabled. */
+ if (device_pm_not_required(dev))
+ return 0;
+
rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
if (rc)
return rc;
- if (pm_runtime_callbacks_present(dev)) {
+ if (!pm_runtime_has_no_callbacks(dev)) {
rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
if (rc)
goto err_out;
@@ -668,8 +716,13 @@ int dpm_sysfs_add(struct device *dev)
if (rc)
goto err_wakeup;
}
+ rc = pm_wakeup_source_sysfs_add(dev);
+ if (rc)
+ goto err_latency;
return 0;
+ err_latency:
+ sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group);
err_wakeup:
sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
err_runtime:
@@ -679,14 +732,59 @@ int dpm_sysfs_add(struct device *dev)
return rc;
}
+int dpm_sysfs_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
+{
+ int rc;
+
+ if (device_pm_not_required(dev))
+ return 0;
+
+ rc = sysfs_group_change_owner(&dev->kobj, &pm_attr_group, kuid, kgid);
+ if (rc)
+ return rc;
+
+ if (!pm_runtime_has_no_callbacks(dev)) {
+ rc = sysfs_group_change_owner(
+ &dev->kobj, &pm_runtime_attr_group, kuid, kgid);
+ if (rc)
+ return rc;
+ }
+
+ if (device_can_wakeup(dev)) {
+ rc = sysfs_group_change_owner(&dev->kobj, &pm_wakeup_attr_group,
+ kuid, kgid);
+ if (rc)
+ return rc;
+
+ rc = dpm_sysfs_wakeup_change_owner(dev, kuid, kgid);
+ if (rc)
+ return rc;
+ }
+
+ if (dev->power.set_latency_tolerance) {
+ rc = sysfs_group_change_owner(
+ &dev->kobj, &pm_qos_latency_tolerance_attr_group, kuid,
+ kgid);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
int wakeup_sysfs_add(struct device *dev)
{
- return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
+ int ret = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
+
+ if (!ret)
+ kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+
+ return ret;
}
void wakeup_sysfs_remove(struct device *dev)
{
sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
+ kobject_uevent(&dev->kobj, KOBJ_CHANGE);
}
int pm_qos_sysfs_add_resume_latency(struct device *dev)
@@ -727,6 +825,8 @@ void rpm_sysfs_remove(struct device *dev)
void dpm_sysfs_remove(struct device *dev)
{
+ if (device_pm_not_required(dev))
+ return;
sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group);
dev_pm_qos_constraints_destroy(dev);
rpm_sysfs_remove(dev);