PM: Do not create wakeup sysfs files for devices that cannot wake up

Currently, wakeup sysfs attributes are created for all devices,
regardless of whether or not they are wakeup-capable.  This is
excessive and complicates wakeup device identification from user
space (i.e. to identify wakeup-capable devices user space has to read
/sys/devices/.../power/wakeup for all devices and see if they are not
empty).

Fix this issue by avoiding to create wakeup sysfs files for devices
that cannot wake up the system from sleep states (i.e. whose
power.can_wakeup flags are unset during registration) and modify
device_set_wakeup_capable() so that it adds (or removes) the relevant
sysfs attributes if a device's wakeup capability status is changed.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 698dde7..f2a25f1 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -58,19 +58,18 @@
  * sysfs.c
  */
 
-extern int dpm_sysfs_add(struct device *);
-extern void dpm_sysfs_remove(struct device *);
-extern void rpm_sysfs_remove(struct device *);
+extern int dpm_sysfs_add(struct device *dev);
+extern void dpm_sysfs_remove(struct device *dev);
+extern void rpm_sysfs_remove(struct device *dev);
+extern int wakeup_sysfs_add(struct device *dev);
+extern void wakeup_sysfs_remove(struct device *dev);
 
 #else /* CONFIG_PM */
 
-static inline int dpm_sysfs_add(struct device *dev)
-{
-	return 0;
-}
-
-static inline void dpm_sysfs_remove(struct device *dev)
-{
-}
+static inline int dpm_sysfs_add(struct device *dev) { return 0; }
+static inline void dpm_sysfs_remove(struct device *dev) {}
+static inline void rpm_sysfs_remove(struct device *dev) {}
+static inline int wakeup_sysfs_add(struct device *dev) { return 0; }
+static inline void wakeup_sysfs_remove(struct device *dev) {}
 
 #endif
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 0b1e46b..fff49be 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -431,9 +431,28 @@
 static DEVICE_ATTR(async, 0644, async_show, async_store);
 #endif /* CONFIG_PM_ADVANCED_DEBUG */
 
-static struct attribute * power_attrs[] = {
-	&dev_attr_wakeup.attr,
+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 /* CONFIG_PM_ADVANCED_DEBUG */
+	NULL,
+};
+static struct attribute_group pm_attr_group = {
+	.name	= power_group_name,
+	.attrs	= power_attrs,
+};
+
+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_hit_count.attr,
@@ -442,25 +461,15 @@
 	&dev_attr_wakeup_max_time_ms.attr,
 	&dev_attr_wakeup_last_time_ms.attr,
 #endif
-#ifdef CONFIG_PM_ADVANCED_DEBUG
-	&dev_attr_async.attr,
-#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
 	NULL,
 };
-static struct attribute_group pm_attr_group = {
+static struct attribute_group pm_wakeup_attr_group = {
 	.name	= power_group_name,
-	.attrs	= power_attrs,
+	.attrs	= wakeup_attrs,
 };
 
-#ifdef CONFIG_PM_RUNTIME
-
 static struct attribute *runtime_attrs[] = {
+#ifdef CONFIG_PM_RUNTIME
 #ifndef CONFIG_PM_ADVANCED_DEBUG
 	&dev_attr_runtime_status.attr,
 #endif
@@ -468,6 +477,7 @@
 	&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 = {
@@ -480,14 +490,41 @@
 	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) {
+			if (pm_runtime_callbacks_present(dev))
+				sysfs_unmerge_group(&dev->kobj,
+						    &pm_runtime_attr_group);
+			goto err_out;
+		}
+	}
+	return 0;
+
+ err_out:
+	sysfs_remove_group(&dev->kobj, &pm_attr_group);
 	return rc;
 }
 
+int wakeup_sysfs_add(struct device *dev)
+{
+	return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
+}
+
+void wakeup_sysfs_remove(struct device *dev)
+{
+	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
+}
+
 void rpm_sysfs_remove(struct device *dev)
 {
 	sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
@@ -496,19 +533,6 @@
 void dpm_sysfs_remove(struct device *dev)
 {
 	rpm_sysfs_remove(dev);
+	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
 	sysfs_remove_group(&dev->kobj, &pm_attr_group);
 }
-
-#else /* CONFIG_PM_RUNTIME */
-
-int dpm_sysfs_add(struct device * dev)
-{
-	return sysfs_create_group(&dev->kobj, &pm_attr_group);
-}
-
-void dpm_sysfs_remove(struct device * dev)
-{
-	sysfs_remove_group(&dev->kobj, &pm_attr_group);
-}
-
-#endif
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 8283638..4573c83 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -242,6 +242,35 @@
 EXPORT_SYMBOL_GPL(device_wakeup_disable);
 
 /**
+ * device_set_wakeup_capable - Set/reset device wakeup capability flag.
+ * @dev: Device to handle.
+ * @capable: Whether or not @dev is capable of waking up the system from sleep.
+ *
+ * If @capable is set, set the @dev's power.can_wakeup flag and add its
+ * wakeup-related attributes to sysfs.  Otherwise, unset the @dev's
+ * power.can_wakeup flag and remove its wakeup-related attributes from sysfs.
+ *
+ * This function may sleep and it can't be called from any context where
+ * sleeping is not allowed.
+ */
+void device_set_wakeup_capable(struct device *dev, bool capable)
+{
+	if (!!dev->power.can_wakeup == !!capable)
+		return;
+
+	if (device_is_registered(dev)) {
+		if (capable) {
+			if (wakeup_sysfs_add(dev))
+				return;
+		} else {
+			wakeup_sysfs_remove(dev);
+		}
+	}
+	dev->power.can_wakeup = capable;
+}
+EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
+
+/**
  * device_init_wakeup - Device wakeup initialization.
  * @dev: Device to handle.
  * @enable: Whether or not to enable @dev as a wakeup device.