===================================================================
@@ -273,6 +273,280 @@ static int pm_genpd_runtime_resume(struc
#endif /* CONFIG_PM_RUNTIME */
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_powered_down - Check if power has been removed from a power domain.
+ * @genpd: Power domain to check.
+ */
+static bool pm_genpd_powered_down(struct generic_power_domain *genpd)
+{
+ bool ret;
+
+ mutex_lock(&genpd->lock);
+ ret = genpd->power_is_off;
+ mutex_unlock(&genpd->lock);
+
+ return ret;
+}
+
+/**
+ * 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ if (pm_genpd_powered_down(genpd))
+ 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ if (pm_genpd_powered_down(genpd))
+ 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ if (pm_genpd_powered_down(genpd))
+ 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ return pm_genpd_powered_down(genpd) ? 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ if (pm_genpd_powered_down(genpd))
+ 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ if (!pm_genpd_powered_down(genpd) && 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ if (!pm_genpd_powered_down(genpd) && 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__);
+
+ if (IS_ERR_OR_NULL(dev->pwr_domain))
+ return -EINVAL;
+
+ genpd = container_of(dev->pwr_domain,
+ struct generic_power_domain, domain);
+
+ return pm_genpd_powered_down(genpd) ? 0 : pm_generic_thaw(dev);
+}
+
+#else
+
+#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
+
+#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 +578,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 +616,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 +714,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_generic_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_generic_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);