===================================================================
@@ -14,6 +14,19 @@
#include <linux/slab.h>
#include <linux/err.h>
+#ifdef CONFIG_PM
+
+static struct generic_power_domain *dev_to_genpd(struct device *dev)
+{
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return ERR_PTR(-EINVAL);
+
+ return container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+}
+
+#endif /* CONFIG_PM */
+
#ifdef CONFIG_PM_RUNTIME
/**
@@ -143,12 +156,10 @@ static int pm_genpd_runtime_suspend(stru
dev_dbg(dev, "%s()\n", __func__);
- if (IS_ERR_OR_NULL(dev->pwr_domain))
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
return -EINVAL;
- genpd = container_of(dev->pwr_domain,
- struct generic_power_domain, domain);
-
mutex_lock(&genpd->lock);
if (genpd->stop_device) {
@@ -250,12 +261,10 @@ static int pm_genpd_runtime_resume(struc
dev_dbg(dev, "%s()\n", __func__);
- if (IS_ERR_OR_NULL(dev->pwr_domain))
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
return -EINVAL;
- genpd = container_of(dev->pwr_domain,
- struct generic_power_domain, domain);
-
ret = pm_genpd_poweron(genpd);
if (ret)
return ret;
@@ -273,6 +282,293 @@ static int pm_genpd_runtime_resume(struc
#endif /* CONFIG_PM_RUNTIME */
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a power domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pwr_domain field points to the domain member of
+ * an object of type struct generic_power_domain representing a power domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->power_is_off ? 0 : pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O power domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pwr_domain field points to the
+ * domain member of an object of type struct generic_power_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->power_is_off)
+ return 0;
+
+ /*
+ * If the device is in the (runtime) "suspended" state, call
+ * ->start_device() for it, if defined.
+ */
+ pm_runtime_resume(dev);
+
+ return pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O power domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pwr_domain field points to the domain member of an object of type
+ * struct generic_power_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->power_is_off)
+ return 0;
+
+ if (genpd->stop_device)
+ genpd->stop_device(dev);
+
+ mutex_lock(&genpd->lock);
+ if (++genpd->suspended_count == genpd->device_count) {
+ if (genpd->power_off)
+ genpd->power_off(&genpd->domain);
+ }
+ mutex_unlock(&genpd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pwr_domain field points to the domain member of an object of type
+ * struct generic_power_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->power_is_off)
+ return 0;
+
+ mutex_lock(&genpd->lock);
+ if (genpd->suspended_count == genpd->device_count) {
+ if (genpd->power_on) {
+ int ret = genpd->power_on(&genpd->domain);
+ if (ret) {
+ mutex_unlock(&genpd->lock);
+ return ret;
+ }
+ }
+ }
+ genpd->suspended_count--;
+ mutex_unlock(&genpd->lock);
+
+ if (genpd->start_device)
+ genpd->start_device(dev);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pwr_domain field points to the
+ * domain member of an object of type struct generic_power_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->power_is_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pwr_domain field points to the
+ * domain member of an object of type struct generic_power_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (genpd->power_is_off)
+ return 0;
+
+ /*
+ * If the device is in the (runtime) "suspended" state, call
+ * ->start_device() for it, if defined.
+ */
+ pm_runtime_resume(dev);
+
+ return pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pwr_domain field points to the domain member of an object of type
+ * struct generic_power_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (!genpd->power_is_off && genpd->stop_device)
+ genpd->stop_device(dev);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pwr_domain field points to the domain member of an object of type
+ * struct generic_power_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ if (!genpd->power_is_off && genpd->start_device)
+ genpd->start_device(dev);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pwr_domain field points to the
+ * domain member of an object of type struct generic_power_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (IS_ERR(genpd))
+ return -EINVAL;
+
+ return genpd->power_is_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pwr_domain field points to the
+ * domain member of an object of type struct generic_power_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+ struct generic_power_domain *genpd;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ genpd = dev_to_genpd(dev);
+ if (!IS_ERR(genpd) && !genpd->power_is_off)
+ pm_generic_complete(dev);
+}
+
+#else
+
+#define pm_genpd_prepare NULL
+#define pm_genpd_suspend NULL
+#define pm_genpd_suspend_noirq NULL
+#define pm_genpd_resume_noirq NULL
+#define pm_genpd_resume NULL
+#define pm_genpd_freeze NULL
+#define pm_genpd_freeze_noirq NULL
+#define pm_genpd_thaw_noirq NULL
+#define pm_genpd_thaw NULL
+#define pm_genpd_complete NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
/**
* pm_genpd_add_device - Add a device to an I/O power domain.
* @genpd: Power domain to add the device to.
@@ -304,6 +600,7 @@ int pm_genpd_add_device(struct generic_p
dle->dev = dev;
list_add_tail(&dle->node, &genpd->device_list);
+ genpd->device_count++;
spin_lock_irq(&dev->power.lock);
dev->pwr_domain = &genpd->domain;
@@ -341,6 +638,7 @@ int pm_genpd_remove_device(struct generi
dev->pwr_domain = NULL;
spin_unlock_irq(&dev->power.lock);
+ genpd->device_count--;
list_del(&dle->node);
kfree(dle);
@@ -438,7 +736,23 @@ void pm_genpd_init(struct generic_power_
genpd->gov = gov;
genpd->in_progress = 0;
genpd->power_is_off = is_off;
+ genpd->device_count = 0;
+ genpd->suspended_count = 0;
genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+ genpd->domain.ops.prepare = pm_genpd_prepare;
+ genpd->domain.ops.suspend = pm_genpd_suspend;
+ genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+ genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+ genpd->domain.ops.resume = pm_genpd_resume;
+ genpd->domain.ops.freeze = pm_genpd_freeze;
+ genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+ genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+ genpd->domain.ops.thaw = pm_genpd_thaw;
+ genpd->domain.ops.poweroff = pm_genpd_suspend;
+ genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
+ genpd->domain.ops.restore_noirq = pm_genpd_resume_noirq;
+ genpd->domain.ops.restore = pm_genpd_resume;
+ genpd->domain.ops.complete = pm_genpd_complete;
}
===================================================================
@@ -25,6 +25,8 @@ struct generic_power_domain {
struct dev_power_governor *gov;
unsigned int in_progress;
bool power_is_off;
+ unsigned int device_count;
+ unsigned int suspended_count;
int (*power_off)(struct dev_power_domain *domain);
int (*power_on)(struct dev_power_domain *domain);
int (*start_device)(struct device *dev);