diff options
Diffstat (limited to 'drivers/base/power/sysfs.c')
| -rw-r--r-- | drivers/base/power/sysfs.c | 324 | 
1 files changed, 289 insertions, 35 deletions
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 0b1e46bf3e5..95b181d1ca6 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -4,8 +4,10 @@  #include <linux/device.h>  #include <linux/string.h> +#include <linux/export.h> +#include <linux/pm_qos.h>  #include <linux/pm_runtime.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <linux/jiffies.h>  #include "power.h" @@ -116,12 +118,14 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,  	cp = memchr(buf, '\n', n);  	if (cp)  		len = cp - buf; +	device_lock(dev);  	if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)  		pm_runtime_allow(dev);  	else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)  		pm_runtime_forbid(dev);  	else -		return -EINVAL; +		n = -EINVAL; +	device_unlock(dev);  	return n;  } @@ -202,18 +206,136 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,  	if (!dev->power.use_autosuspend)  		return -EIO; -	if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay) +	if (kstrtol(buf, 10, &delay) != 0 || delay != (int) delay)  		return -EINVAL; +	device_lock(dev);  	pm_runtime_set_autosuspend_delay(dev, delay); +	device_unlock(dev);  	return n;  }  static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,  		autosuspend_delay_ms_store); -#endif +static ssize_t pm_qos_resume_latency_show(struct device *dev, +					  struct device_attribute *attr, +					  char *buf) +{ +	return sprintf(buf, "%d\n", dev_pm_qos_requested_resume_latency(dev)); +} + +static ssize_t pm_qos_resume_latency_store(struct device *dev, +					   struct device_attribute *attr, +					   const char *buf, size_t n) +{ +	s32 value; +	int ret; + +	if (kstrtos32(buf, 0, &value)) +		return -EINVAL; + +	if (value < 0) +		return -EINVAL; +	ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req, +					value); +	return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, +		   pm_qos_resume_latency_show, pm_qos_resume_latency_store); + +static ssize_t pm_qos_latency_tolerance_show(struct device *dev, +					     struct device_attribute *attr, +					     char *buf) +{ +	s32 value = dev_pm_qos_get_user_latency_tolerance(dev); + +	if (value < 0) +		return sprintf(buf, "auto\n"); +	else if (value == PM_QOS_LATENCY_ANY) +		return sprintf(buf, "any\n"); + +	return sprintf(buf, "%d\n", value); +} + +static ssize_t pm_qos_latency_tolerance_store(struct device *dev, +					      struct device_attribute *attr, +					      const char *buf, size_t n) +{ +	s32 value; +	int ret; + +	if (kstrtos32(buf, 0, &value)) { +		if (!strcmp(buf, "auto") || !strcmp(buf, "auto\n")) +			value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; +		else if (!strcmp(buf, "any") || !strcmp(buf, "any\n")) +			value = PM_QOS_LATENCY_ANY; +	} +	ret = dev_pm_qos_update_user_latency_tolerance(dev, value); +	return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_latency_tolerance_us, 0644, +		   pm_qos_latency_tolerance_show, pm_qos_latency_tolerance_store); + +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)); +} + +static ssize_t pm_qos_no_power_off_store(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, size_t n) +{ +	int ret; + +	if (kstrtoint(buf, 0, &ret)) +		return -EINVAL; + +	if (ret != 0 && ret != 1) +		return -EINVAL; + +	ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_NO_POWER_OFF, ret); +	return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_no_power_off, 0644, +		   pm_qos_no_power_off_show, pm_qos_no_power_off_store); + +static ssize_t pm_qos_remote_wakeup_show(struct device *dev, +					 struct device_attribute *attr, +					 char *buf) +{ +	return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) +					& PM_QOS_FLAG_REMOTE_WAKEUP)); +} + +static ssize_t pm_qos_remote_wakeup_store(struct device *dev, +					  struct device_attribute *attr, +					  const char *buf, size_t n) +{ +	int ret; + +	if (kstrtoint(buf, 0, &ret)) +		return -EINVAL; + +	if (ret != 0 && ret != 1) +		return -EINVAL; + +	ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); +	return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, +		   pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_SLEEP  static ssize_t  wake_show(struct device * dev, struct device_attribute *attr, char * buf)  { @@ -248,7 +370,6 @@ wake_store(struct device * dev, struct device_attribute *attr,  static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); -#ifdef CONFIG_PM_SLEEP  static ssize_t wakeup_count_show(struct device *dev,  				struct device_attribute *attr, char *buf)  { @@ -283,22 +404,41 @@ static ssize_t wakeup_active_count_show(struct device *dev,  static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); -static ssize_t wakeup_hit_count_show(struct device *dev, -				struct device_attribute *attr, char *buf) +static ssize_t wakeup_abort_count_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	unsigned long count = 0; +	bool enabled = false; + +	spin_lock_irq(&dev->power.lock); +	if (dev->power.wakeup) { +		count = dev->power.wakeup->wakeup_count; +		enabled = true; +	} +	spin_unlock_irq(&dev->power.lock); +	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL); + +static ssize_t wakeup_expire_count_show(struct device *dev, +					struct device_attribute *attr, +					char *buf)  {  	unsigned long count = 0;  	bool enabled = false;  	spin_lock_irq(&dev->power.lock);  	if (dev->power.wakeup) { -		count = dev->power.wakeup->hit_count; +		count = dev->power.wakeup->expire_count;  		enabled = true;  	}  	spin_unlock_irq(&dev->power.lock);  	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");  } -static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); +static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);  static ssize_t wakeup_active_show(struct device *dev,  				struct device_attribute *attr, char *buf) @@ -367,6 +507,27 @@ static ssize_t wakeup_last_time_show(struct device *dev,  }  static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); + +#ifdef CONFIG_PM_AUTOSLEEP +static ssize_t wakeup_prevent_sleep_time_show(struct device *dev, +					      struct device_attribute *attr, +					      char *buf) +{ +	s64 msec = 0; +	bool enabled = false; + +	spin_lock_irq(&dev->power.lock); +	if (dev->power.wakeup) { +		msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time); +		enabled = true; +	} +	spin_unlock_irq(&dev->power.lock); +	return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444, +		   wakeup_prevent_sleep_time_show, NULL); +#endif /* CONFIG_PM_AUTOSLEEP */  #endif /* CONFIG_PM_SLEEP */  #ifdef CONFIG_PM_ADVANCED_DEBUG @@ -403,6 +564,8 @@ static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL);  #endif +#ifdef CONFIG_PM_SLEEP +  static ssize_t async_show(struct device *dev, struct device_attribute *attr,  			  char *buf)  { @@ -429,28 +592,22 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr,  }  static DEVICE_ATTR(async, 0644, async_show, async_store); -#endif /* CONFIG_PM_ADVANCED_DEBUG */ -static struct attribute * power_attrs[] = { -	&dev_attr_wakeup.attr, -#ifdef CONFIG_PM_SLEEP -	&dev_attr_wakeup_count.attr, -	&dev_attr_wakeup_active_count.attr, -	&dev_attr_wakeup_hit_count.attr, -	&dev_attr_wakeup_active.attr, -	&dev_attr_wakeup_total_time_ms.attr, -	&dev_attr_wakeup_max_time_ms.attr, -	&dev_attr_wakeup_last_time_ms.attr,  #endif +#endif /* CONFIG_PM_ADVANCED_DEBUG */ + +static struct attribute *power_attrs[] = {  #ifdef CONFIG_PM_ADVANCED_DEBUG +#ifdef CONFIG_PM_SLEEP  	&dev_attr_async.attr, +#endif  #ifdef CONFIG_PM_RUNTIME  	&dev_attr_runtime_status.attr,  	&dev_attr_runtime_usage.attr,  	&dev_attr_runtime_active_kids.attr,  	&dev_attr_runtime_enabled.attr,  #endif -#endif +#endif /* CONFIG_PM_ADVANCED_DEBUG */  	NULL,  };  static struct attribute_group pm_attr_group = { @@ -458,9 +615,30 @@ static struct attribute_group pm_attr_group = {  	.attrs	= power_attrs,  }; -#ifdef CONFIG_PM_RUNTIME +static struct attribute *wakeup_attrs[] = { +#ifdef CONFIG_PM_SLEEP +	&dev_attr_wakeup.attr, +	&dev_attr_wakeup_count.attr, +	&dev_attr_wakeup_active_count.attr, +	&dev_attr_wakeup_abort_count.attr, +	&dev_attr_wakeup_expire_count.attr, +	&dev_attr_wakeup_active.attr, +	&dev_attr_wakeup_total_time_ms.attr, +	&dev_attr_wakeup_max_time_ms.attr, +	&dev_attr_wakeup_last_time_ms.attr, +#ifdef CONFIG_PM_AUTOSLEEP +	&dev_attr_wakeup_prevent_sleep_time_ms.attr, +#endif +#endif +	NULL, +}; +static struct attribute_group pm_wakeup_attr_group = { +	.name	= power_group_name, +	.attrs	= wakeup_attrs, +};  static struct attribute *runtime_attrs[] = { +#ifdef CONFIG_PM_RUNTIME  #ifndef CONFIG_PM_ADVANCED_DEBUG  	&dev_attr_runtime_status.attr,  #endif @@ -468,6 +646,7 @@ static struct attribute *runtime_attrs[] = {  	&dev_attr_runtime_suspended_time.attr,  	&dev_attr_runtime_active_time.attr,  	&dev_attr_autosuspend_delay_ms.attr, +#endif /* CONFIG_PM_RUNTIME */  	NULL,  };  static struct attribute_group pm_runtime_attr_group = { @@ -475,40 +654,115 @@ static struct attribute_group pm_runtime_attr_group = {  	.attrs	= runtime_attrs,  }; +static struct attribute *pm_qos_resume_latency_attrs[] = { +#ifdef CONFIG_PM_RUNTIME +	&dev_attr_pm_qos_resume_latency_us.attr, +#endif /* CONFIG_PM_RUNTIME */ +	NULL, +}; +static struct attribute_group pm_qos_resume_latency_attr_group = { +	.name	= power_group_name, +	.attrs	= pm_qos_resume_latency_attrs, +}; + +static struct attribute *pm_qos_latency_tolerance_attrs[] = { +#ifdef CONFIG_PM_RUNTIME +	&dev_attr_pm_qos_latency_tolerance_us.attr, +#endif /* CONFIG_PM_RUNTIME */ +	NULL, +}; +static struct attribute_group pm_qos_latency_tolerance_attr_group = { +	.name	= power_group_name, +	.attrs	= pm_qos_latency_tolerance_attrs, +}; + +static struct attribute *pm_qos_flags_attrs[] = { +#ifdef CONFIG_PM_RUNTIME +	&dev_attr_pm_qos_no_power_off.attr, +	&dev_attr_pm_qos_remote_wakeup.attr, +#endif /* CONFIG_PM_RUNTIME */ +	NULL, +}; +static struct attribute_group pm_qos_flags_attr_group = { +	.name	= power_group_name, +	.attrs	= pm_qos_flags_attrs, +}; +  int dpm_sysfs_add(struct device *dev)  {  	int rc;  	rc = sysfs_create_group(&dev->kobj, &pm_attr_group); -	if (rc == 0 && !dev->power.no_callbacks) { +	if (rc) +		return rc; + +	if (pm_runtime_callbacks_present(dev)) {  		rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);  		if (rc) -			sysfs_remove_group(&dev->kobj, &pm_attr_group); +			goto err_out; +	} +	if (device_can_wakeup(dev)) { +		rc = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group); +		if (rc) +			goto err_runtime; +	} +	if (dev->power.set_latency_tolerance) { +		rc = sysfs_merge_group(&dev->kobj, +				       &pm_qos_latency_tolerance_attr_group); +		if (rc) +			goto err_wakeup;  	} +	return 0; + + err_wakeup: +	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); + err_runtime: +	sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); + err_out: +	sysfs_remove_group(&dev->kobj, &pm_attr_group);  	return rc;  } -void rpm_sysfs_remove(struct device *dev) +int wakeup_sysfs_add(struct device *dev)  { -	sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); +	return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);  } -void dpm_sysfs_remove(struct device *dev) +void wakeup_sysfs_remove(struct device *dev)  { -	rpm_sysfs_remove(dev); -	sysfs_remove_group(&dev->kobj, &pm_attr_group); +	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); +} + +int pm_qos_sysfs_add_resume_latency(struct device *dev) +{ +	return sysfs_merge_group(&dev->kobj, &pm_qos_resume_latency_attr_group);  } -#else /* CONFIG_PM_RUNTIME */ +void pm_qos_sysfs_remove_resume_latency(struct device *dev) +{ +	sysfs_unmerge_group(&dev->kobj, &pm_qos_resume_latency_attr_group); +} -int dpm_sysfs_add(struct device * dev) +int pm_qos_sysfs_add_flags(struct device *dev)  { -	return sysfs_create_group(&dev->kobj, &pm_attr_group); +	return sysfs_merge_group(&dev->kobj, &pm_qos_flags_attr_group);  } -void dpm_sysfs_remove(struct device * dev) +void pm_qos_sysfs_remove_flags(struct device *dev)  { -	sysfs_remove_group(&dev->kobj, &pm_attr_group); +	sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group);  } -#endif +void rpm_sysfs_remove(struct device *dev) +{ +	sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); +} + +void dpm_sysfs_remove(struct device *dev) +{ +	sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); +	dev_pm_qos_constraints_destroy(dev); +	rpm_sysfs_remove(dev); +	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); +	sysfs_remove_group(&dev->kobj, &pm_attr_group); +}  | 
