xref: /openbmc/linux/drivers/base/power/domain.c (revision cd0ea672)
1f721889fSRafael J. Wysocki /*
2f721889fSRafael J. Wysocki  * drivers/base/power/domain.c - Common code related to device power domains.
3f721889fSRafael J. Wysocki  *
4f721889fSRafael J. Wysocki  * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
5f721889fSRafael J. Wysocki  *
6f721889fSRafael J. Wysocki  * This file is released under the GPLv2.
7f721889fSRafael J. Wysocki  */
8f721889fSRafael J. Wysocki 
9f721889fSRafael J. Wysocki #include <linux/init.h>
10f721889fSRafael J. Wysocki #include <linux/kernel.h>
11f721889fSRafael J. Wysocki #include <linux/io.h>
12f721889fSRafael J. Wysocki #include <linux/pm_runtime.h>
13f721889fSRafael J. Wysocki #include <linux/pm_domain.h>
14f721889fSRafael J. Wysocki #include <linux/slab.h>
15f721889fSRafael J. Wysocki #include <linux/err.h>
1617b75ecaSRafael J. Wysocki #include <linux/sched.h>
1717b75ecaSRafael J. Wysocki #include <linux/suspend.h>
18f721889fSRafael J. Wysocki 
195125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list);
205125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock);
215125bbf3SRafael J. Wysocki 
225248051bSRafael J. Wysocki #ifdef CONFIG_PM
235248051bSRafael J. Wysocki 
245248051bSRafael J. Wysocki static struct generic_pm_domain *dev_to_genpd(struct device *dev)
255248051bSRafael J. Wysocki {
265248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
275248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
285248051bSRafael J. Wysocki 
29596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
305248051bSRafael J. Wysocki }
31f721889fSRafael J. Wysocki 
32c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
33f721889fSRafael J. Wysocki {
34c4bb3160SRafael J. Wysocki 	bool ret = false;
35c4bb3160SRafael J. Wysocki 
36c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
37c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
38c4bb3160SRafael J. Wysocki 
39c4bb3160SRafael J. Wysocki 	return ret;
40c4bb3160SRafael J. Wysocki }
41c4bb3160SRafael J. Wysocki 
42c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
43c4bb3160SRafael J. Wysocki {
44c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
45c4bb3160SRafael J. Wysocki 	smp_mb__after_atomic_inc();
46f721889fSRafael J. Wysocki }
47f721889fSRafael J. Wysocki 
4817b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
4917b75ecaSRafael J. Wysocki {
5017b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
5117b75ecaSRafael J. Wysocki 
5217b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
5317b75ecaSRafael J. Wysocki 	/*
5417b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
5517b75ecaSRafael J. Wysocki 	 * or the power off state.
5617b75ecaSRafael J. Wysocki 	 */
5717b75ecaSRafael J. Wysocki 	for (;;) {
5817b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
5917b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
60c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
61c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
6217b75ecaSRafael J. Wysocki 			break;
6317b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
6417b75ecaSRafael J. Wysocki 
6517b75ecaSRafael J. Wysocki 		schedule();
6617b75ecaSRafael J. Wysocki 
6717b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
6817b75ecaSRafael J. Wysocki 	}
6917b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
7017b75ecaSRafael J. Wysocki }
7117b75ecaSRafael J. Wysocki 
7217b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
7317b75ecaSRafael J. Wysocki {
7417b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
7517b75ecaSRafael J. Wysocki }
7617b75ecaSRafael J. Wysocki 
77c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
78c6d22b37SRafael J. Wysocki {
79c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
80c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
81c6d22b37SRafael J. Wysocki }
82c6d22b37SRafael J. Wysocki 
83f721889fSRafael J. Wysocki /**
845063ce15SRafael J. Wysocki  * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
855248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
865248051bSRafael J. Wysocki  *
875063ce15SRafael J. Wysocki  * Restore power to @genpd and all of its masters so that it is possible to
885248051bSRafael J. Wysocki  * resume a device belonging to it.
895248051bSRafael J. Wysocki  */
903f241775SRafael J. Wysocki int __pm_genpd_poweron(struct generic_pm_domain *genpd)
913f241775SRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
925248051bSRafael J. Wysocki {
935063ce15SRafael J. Wysocki 	struct gpd_link *link;
943f241775SRafael J. Wysocki 	DEFINE_WAIT(wait);
955248051bSRafael J. Wysocki 	int ret = 0;
965248051bSRafael J. Wysocki 
975063ce15SRafael J. Wysocki 	/* If the domain's master is being waited for, we have to wait too. */
983f241775SRafael J. Wysocki 	for (;;) {
993f241775SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
1003f241775SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
10117877eb5SRafael J. Wysocki 		if (genpd->status != GPD_STATE_WAIT_MASTER)
1023f241775SRafael J. Wysocki 			break;
1033f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1043f241775SRafael J. Wysocki 
1053f241775SRafael J. Wysocki 		schedule();
1063f241775SRafael J. Wysocki 
10717b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
1083f241775SRafael J. Wysocki 	}
1093f241775SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
11017b75ecaSRafael J. Wysocki 
11117b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
112596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
1133f241775SRafael J. Wysocki 		return 0;
1145248051bSRafael J. Wysocki 
115c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
116c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
1173f241775SRafael J. Wysocki 		return 0;
118c6d22b37SRafael J. Wysocki 	}
119c6d22b37SRafael J. Wysocki 
1205063ce15SRafael J. Wysocki 	/*
1215063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
1225063ce15SRafael J. Wysocki 	 * executed, unless one of the masters' .power_on() callbacks fiddles
1235063ce15SRafael J. Wysocki 	 * with it.
1245063ce15SRafael J. Wysocki 	 */
1255063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1265063ce15SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
12717877eb5SRafael J. Wysocki 		genpd->status = GPD_STATE_WAIT_MASTER;
1283c07cbc4SRafael J. Wysocki 
1295248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1305248051bSRafael J. Wysocki 
1315063ce15SRafael J. Wysocki 		ret = pm_genpd_poweron(link->master);
1329e08cf42SRafael J. Wysocki 
1339e08cf42SRafael J. Wysocki 		mutex_lock(&genpd->lock);
1349e08cf42SRafael J. Wysocki 
1353f241775SRafael J. Wysocki 		/*
1363f241775SRafael J. Wysocki 		 * The "wait for parent" status is guaranteed not to change
1375063ce15SRafael J. Wysocki 		 * while the master is powering on.
1383f241775SRafael J. Wysocki 		 */
1393f241775SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1403f241775SRafael J. Wysocki 		wake_up_all(&genpd->status_wait_queue);
1415063ce15SRafael J. Wysocki 		if (ret) {
1425063ce15SRafael J. Wysocki 			genpd_sd_counter_dec(link->master);
1439e08cf42SRafael J. Wysocki 			goto err;
1445248051bSRafael J. Wysocki 		}
1455063ce15SRafael J. Wysocki 	}
1465248051bSRafael J. Wysocki 
1479e08cf42SRafael J. Wysocki 	if (genpd->power_on) {
148fe202fdeSRafael J. Wysocki 		ret = genpd->power_on(genpd);
1499e08cf42SRafael J. Wysocki 		if (ret)
1509e08cf42SRafael J. Wysocki 			goto err;
1513c07cbc4SRafael J. Wysocki 	}
1525248051bSRafael J. Wysocki 
1539e08cf42SRafael J. Wysocki 	genpd_set_active(genpd);
1549e08cf42SRafael J. Wysocki 
1553f241775SRafael J. Wysocki 	return 0;
1569e08cf42SRafael J. Wysocki 
1579e08cf42SRafael J. Wysocki  err:
1585063ce15SRafael J. Wysocki 	list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
1595063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
1609e08cf42SRafael J. Wysocki 
1613f241775SRafael J. Wysocki 	return ret;
1623f241775SRafael J. Wysocki }
1633f241775SRafael J. Wysocki 
1643f241775SRafael J. Wysocki /**
1655063ce15SRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its masters.
1663f241775SRafael J. Wysocki  * @genpd: PM domain to power up.
1673f241775SRafael J. Wysocki  */
1683f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd)
1693f241775SRafael J. Wysocki {
1703f241775SRafael J. Wysocki 	int ret;
1713f241775SRafael J. Wysocki 
1723f241775SRafael J. Wysocki 	mutex_lock(&genpd->lock);
1733f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
1743f241775SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1753f241775SRafael J. Wysocki 	return ret;
1765248051bSRafael J. Wysocki }
1775248051bSRafael J. Wysocki 
1785248051bSRafael J. Wysocki #endif /* CONFIG_PM */
1795248051bSRafael J. Wysocki 
1805248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
1815248051bSRafael J. Wysocki 
1825248051bSRafael J. Wysocki /**
183f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
1844605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to save the state of.
185f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
186f721889fSRafael J. Wysocki  */
1874605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd,
188f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
18917b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
190f721889fSRafael J. Wysocki {
191cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1924605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
193f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
194f721889fSRafael J. Wysocki 	int ret = 0;
195f721889fSRafael J. Wysocki 
196cd0ea672SRafael J. Wysocki 	if (gpd_data->need_restore)
197f721889fSRafael J. Wysocki 		return 0;
198f721889fSRafael J. Wysocki 
19917b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
20017b75ecaSRafael J. Wysocki 
201f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_suspend) {
202f721889fSRafael J. Wysocki 		if (genpd->start_device)
203f721889fSRafael J. Wysocki 			genpd->start_device(dev);
204f721889fSRafael J. Wysocki 
205f721889fSRafael J. Wysocki 		ret = drv->pm->runtime_suspend(dev);
206f721889fSRafael J. Wysocki 
207f721889fSRafael J. Wysocki 		if (genpd->stop_device)
208f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
209f721889fSRafael J. Wysocki 	}
210f721889fSRafael J. Wysocki 
21117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
21217b75ecaSRafael J. Wysocki 
213f721889fSRafael J. Wysocki 	if (!ret)
214cd0ea672SRafael J. Wysocki 		gpd_data->need_restore = true;
215f721889fSRafael J. Wysocki 
216f721889fSRafael J. Wysocki 	return ret;
217f721889fSRafael J. Wysocki }
218f721889fSRafael J. Wysocki 
219f721889fSRafael J. Wysocki /**
220f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
2214605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to restore the state of.
222f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
223f721889fSRafael J. Wysocki  */
2244605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
225f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
22617b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
227f721889fSRafael J. Wysocki {
228cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
2294605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
230f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
231f721889fSRafael J. Wysocki 
232cd0ea672SRafael J. Wysocki 	if (!gpd_data->need_restore)
233f721889fSRafael J. Wysocki 		return;
234f721889fSRafael J. Wysocki 
23517b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
23617b75ecaSRafael J. Wysocki 
237f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_resume) {
238f721889fSRafael J. Wysocki 		if (genpd->start_device)
239f721889fSRafael J. Wysocki 			genpd->start_device(dev);
240f721889fSRafael J. Wysocki 
241f721889fSRafael J. Wysocki 		drv->pm->runtime_resume(dev);
242f721889fSRafael J. Wysocki 
243f721889fSRafael J. Wysocki 		if (genpd->stop_device)
244f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
245f721889fSRafael J. Wysocki 	}
246f721889fSRafael J. Wysocki 
24717b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
24817b75ecaSRafael J. Wysocki 
249cd0ea672SRafael J. Wysocki 	gpd_data->need_restore = false;
250f721889fSRafael J. Wysocki }
251f721889fSRafael J. Wysocki 
252f721889fSRafael J. Wysocki /**
253c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
254c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
255c6d22b37SRafael J. Wysocki  *
256c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
257c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
258c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
259c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
260c6d22b37SRafael J. Wysocki  */
261c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
262c6d22b37SRafael J. Wysocki {
26317877eb5SRafael J. Wysocki 	return genpd->status == GPD_STATE_WAIT_MASTER
2643f241775SRafael J. Wysocki 		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
265c6d22b37SRafael J. Wysocki }
266c6d22b37SRafael J. Wysocki 
267c6d22b37SRafael J. Wysocki /**
26856375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
26956375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
27056375fd4SRafael J. Wysocki  *
27156375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
27256375fd4SRafael J. Wysocki  * before.
27356375fd4SRafael J. Wysocki  */
2740bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
27556375fd4SRafael J. Wysocki {
27656375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
27756375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
27856375fd4SRafael J. Wysocki }
27956375fd4SRafael J. Wysocki 
28056375fd4SRafael J. Wysocki /**
281f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
282f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
283f721889fSRafael J. Wysocki  *
284f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
285f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
286f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
287f721889fSRafael J. Wysocki  */
288f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
28917b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
290f721889fSRafael J. Wysocki {
2914605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
2925063ce15SRafael J. Wysocki 	struct gpd_link *link;
293f721889fSRafael J. Wysocki 	unsigned int not_suspended;
294c6d22b37SRafael J. Wysocki 	int ret = 0;
295f721889fSRafael J. Wysocki 
296c6d22b37SRafael J. Wysocki  start:
297c6d22b37SRafael J. Wysocki 	/*
298c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
299c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
3005063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
301c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
3023f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
303c6d22b37SRafael J. Wysocki 	 */
3043f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
30517877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
3063f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
307f721889fSRafael J. Wysocki 		return 0;
308f721889fSRafael J. Wysocki 
309c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
310f721889fSRafael J. Wysocki 		return -EBUSY;
311f721889fSRafael J. Wysocki 
312f721889fSRafael J. Wysocki 	not_suspended = 0;
3134605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
3140aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
3150aa2a221SRafael J. Wysocki 		    || pdd->dev->power.irq_safe))
316f721889fSRafael J. Wysocki 			not_suspended++;
317f721889fSRafael J. Wysocki 
318f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
319f721889fSRafael J. Wysocki 		return -EBUSY;
320f721889fSRafael J. Wysocki 
321c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
322c6d22b37SRafael J. Wysocki 		/*
323c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
324c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
325c6d22b37SRafael J. Wysocki 		 */
326c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
327c6d22b37SRafael J. Wysocki 		return 0;
328c6d22b37SRafael J. Wysocki 	}
329c6d22b37SRafael J. Wysocki 
330f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
331f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
332f721889fSRafael J. Wysocki 			return -EAGAIN;
333f721889fSRafael J. Wysocki 	}
334f721889fSRafael J. Wysocki 
33517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
336c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
33717b75ecaSRafael J. Wysocki 
3384605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
3393c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
3404605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
3413f241775SRafael J. Wysocki 
3423f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
3433f241775SRafael J. Wysocki 			goto out;
3443f241775SRafael J. Wysocki 
345697a7f37SRafael J. Wysocki 		if (ret) {
346697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
347697a7f37SRafael J. Wysocki 			goto out;
348697a7f37SRafael J. Wysocki 		}
349f721889fSRafael J. Wysocki 
350c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
351c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
352c6d22b37SRafael J. Wysocki 			goto start;
353c6d22b37SRafael J. Wysocki 		}
354c6d22b37SRafael J. Wysocki 	}
35517b75ecaSRafael J. Wysocki 
3563c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
3573c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
3583c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
359c6d22b37SRafael J. Wysocki 			goto out;
360c6d22b37SRafael J. Wysocki 		}
36117b75ecaSRafael J. Wysocki 
3623c07cbc4SRafael J. Wysocki 		/*
3635063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
3645063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
3653c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
3663c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
3673c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
3683c07cbc4SRafael J. Wysocki 		 * happen very often).
3693c07cbc4SRafael J. Wysocki 		 */
370d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
371d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
372d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
373d2805402SRafael J. Wysocki 			goto out;
374d2805402SRafael J. Wysocki 		}
375d2805402SRafael J. Wysocki 	}
376f721889fSRafael J. Wysocki 
37717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
378f721889fSRafael J. Wysocki 
3795063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
3805063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
3815063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
3825063ce15SRafael J. Wysocki 	}
38317b75ecaSRafael J. Wysocki 
384c6d22b37SRafael J. Wysocki  out:
385c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
386c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
387c6d22b37SRafael J. Wysocki 	return ret;
388f721889fSRafael J. Wysocki }
389f721889fSRafael J. Wysocki 
390f721889fSRafael J. Wysocki /**
391f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
392f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
393f721889fSRafael J. Wysocki  */
394f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
395f721889fSRafael J. Wysocki {
396f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
397f721889fSRafael J. Wysocki 
398f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
399f721889fSRafael J. Wysocki 
40017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
401f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
40217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
403f721889fSRafael J. Wysocki }
404f721889fSRafael J. Wysocki 
405f721889fSRafael J. Wysocki /**
406f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
407f721889fSRafael J. Wysocki  * @dev: Device to suspend.
408f721889fSRafael J. Wysocki  *
409f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
410f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
411f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
412f721889fSRafael J. Wysocki  */
413f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
414f721889fSRafael J. Wysocki {
415f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
416f721889fSRafael J. Wysocki 
417f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
418f721889fSRafael J. Wysocki 
4195248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
4205248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
421f721889fSRafael J. Wysocki 		return -EINVAL;
422f721889fSRafael J. Wysocki 
4230aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
4240aa2a221SRafael J. Wysocki 
425f721889fSRafael J. Wysocki 	if (genpd->stop_device) {
426f721889fSRafael J. Wysocki 		int ret = genpd->stop_device(dev);
427f721889fSRafael J. Wysocki 		if (ret)
42817b75ecaSRafael J. Wysocki 			return ret;
429f721889fSRafael J. Wysocki 	}
43017b75ecaSRafael J. Wysocki 
4310aa2a221SRafael J. Wysocki 	/*
4320aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
4330aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
4340aa2a221SRafael J. Wysocki 	 */
4350aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
4360aa2a221SRafael J. Wysocki 		return 0;
4370aa2a221SRafael J. Wysocki 
438c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
439f721889fSRafael J. Wysocki 	genpd->in_progress++;
440f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
441f721889fSRafael J. Wysocki 	genpd->in_progress--;
442c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
443f721889fSRafael J. Wysocki 
444f721889fSRafael J. Wysocki 	return 0;
445f721889fSRafael J. Wysocki }
446f721889fSRafael J. Wysocki 
447f721889fSRafael J. Wysocki /**
448f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
449f721889fSRafael J. Wysocki  * @dev: Device to resume.
450f721889fSRafael J. Wysocki  *
451f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
452f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
453f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
454f721889fSRafael J. Wysocki  */
455f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
456f721889fSRafael J. Wysocki {
457f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
458c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
459f721889fSRafael J. Wysocki 	int ret;
460f721889fSRafael J. Wysocki 
461f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
462f721889fSRafael J. Wysocki 
4635248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
4645248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
465f721889fSRafael J. Wysocki 		return -EINVAL;
466f721889fSRafael J. Wysocki 
4670aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
4680aa2a221SRafael J. Wysocki 
4690aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
4700aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
4710aa2a221SRafael J. Wysocki 		goto out;
4720aa2a221SRafael J. Wysocki 
473c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
4743f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
4753f241775SRafael J. Wysocki 	if (ret) {
4763f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
4773f241775SRafael J. Wysocki 		return ret;
4783f241775SRafael J. Wysocki 	}
47917b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
480c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
481c6d22b37SRafael J. Wysocki 	for (;;) {
482c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
483c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
484c6d22b37SRafael J. Wysocki 		/*
485c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
486c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
487c6d22b37SRafael J. Wysocki 		 * not wait.
488c6d22b37SRafael J. Wysocki 		 */
489c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
490c6d22b37SRafael J. Wysocki 			break;
491c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
492c6d22b37SRafael J. Wysocki 
493c6d22b37SRafael J. Wysocki 		schedule();
494c6d22b37SRafael J. Wysocki 
495c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
496c6d22b37SRafael J. Wysocki 	}
497c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
498cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
499c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
500c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
50117b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
502c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
50317b75ecaSRafael J. Wysocki 
5040aa2a221SRafael J. Wysocki  out:
50517b75ecaSRafael J. Wysocki 	if (genpd->start_device)
50617b75ecaSRafael J. Wysocki 		genpd->start_device(dev);
507f721889fSRafael J. Wysocki 
508f721889fSRafael J. Wysocki 	return 0;
509f721889fSRafael J. Wysocki }
510f721889fSRafael J. Wysocki 
51117f2ae7fSRafael J. Wysocki /**
51217f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
51317f2ae7fSRafael J. Wysocki  */
51417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
51517f2ae7fSRafael J. Wysocki {
51617f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
51717f2ae7fSRafael J. Wysocki 
51817f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
51917f2ae7fSRafael J. Wysocki 
52017f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
52117f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
52217f2ae7fSRafael J. Wysocki 
52317f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
52417f2ae7fSRafael J. Wysocki }
52517f2ae7fSRafael J. Wysocki 
526f721889fSRafael J. Wysocki #else
527f721889fSRafael J. Wysocki 
528f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
529f721889fSRafael J. Wysocki 
530f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
531f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
532f721889fSRafael J. Wysocki 
533f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
534f721889fSRafael J. Wysocki 
535596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
536596ba34bSRafael J. Wysocki 
537596ba34bSRafael J. Wysocki /**
5385063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
539596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
540596ba34bSRafael J. Wysocki  *
541596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
5425063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
543596ba34bSRafael J. Wysocki  *
544596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
545596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
546596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
547596ba34bSRafael J. Wysocki  */
548596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
549596ba34bSRafael J. Wysocki {
5505063ce15SRafael J. Wysocki 	struct gpd_link *link;
551596ba34bSRafael J. Wysocki 
55217b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
553596ba34bSRafael J. Wysocki 		return;
554596ba34bSRafael J. Wysocki 
555c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
556c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
557596ba34bSRafael J. Wysocki 		return;
558596ba34bSRafael J. Wysocki 
559596ba34bSRafael J. Wysocki 	if (genpd->power_off)
560596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
561596ba34bSRafael J. Wysocki 
56217b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
5635063ce15SRafael J. Wysocki 
5645063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
5655063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
5665063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
567596ba34bSRafael J. Wysocki 	}
568596ba34bSRafael J. Wysocki }
569596ba34bSRafael J. Wysocki 
570596ba34bSRafael J. Wysocki /**
5714ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
5724ecd6e65SRafael J. Wysocki  * @dev: Device to check.
5734ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
5744ecd6e65SRafael J. Wysocki  *
5754ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
5764ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
5774ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
5784ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
5794ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
5804ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
5814ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
5824ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
5834ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
5844ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
5854ecd6e65SRafael J. Wysocki  */
5864ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
5874ecd6e65SRafael J. Wysocki {
5884ecd6e65SRafael J. Wysocki 	bool active_wakeup;
5894ecd6e65SRafael J. Wysocki 
5904ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
5914ecd6e65SRafael J. Wysocki 		return false;
5924ecd6e65SRafael J. Wysocki 
5934ecd6e65SRafael J. Wysocki 	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
5944ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
5954ecd6e65SRafael J. Wysocki }
5964ecd6e65SRafael J. Wysocki 
5974ecd6e65SRafael J. Wysocki /**
598596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
599596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
600596ba34bSRafael J. Wysocki  *
601596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
602596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
603596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
604596ba34bSRafael J. Wysocki  * consisting of I/O devices.
605596ba34bSRafael J. Wysocki  */
606596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
607596ba34bSRafael J. Wysocki {
608596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
609b6c10c84SRafael J. Wysocki 	int ret;
610596ba34bSRafael J. Wysocki 
611596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
612596ba34bSRafael J. Wysocki 
613596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
614596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
615596ba34bSRafael J. Wysocki 		return -EINVAL;
616596ba34bSRafael J. Wysocki 
61717b75ecaSRafael J. Wysocki 	/*
61817b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
61917b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
62017b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
62117b75ecaSRafael J. Wysocki 	 */
62217b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
62317b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
62417b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
62517b75ecaSRafael J. Wysocki 
62617b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
62717b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
62817b75ecaSRafael J. Wysocki 		return -EBUSY;
62917b75ecaSRafael J. Wysocki 	}
63017b75ecaSRafael J. Wysocki 
6314ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
6324ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
6334ecd6e65SRafael J. Wysocki 
63417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
635596ba34bSRafael J. Wysocki 
636596ba34bSRafael J. Wysocki 	if (genpd->prepared_count++ == 0)
63717b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
63817b75ecaSRafael J. Wysocki 
63917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
640596ba34bSRafael J. Wysocki 
641596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
64217b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
643596ba34bSRafael J. Wysocki 		return 0;
644596ba34bSRafael J. Wysocki 	}
645596ba34bSRafael J. Wysocki 
646596ba34bSRafael J. Wysocki 	/*
64717b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
64817b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
64917b75ecaSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by .stop_device()), we need
65017b75ecaSRafael J. Wysocki 	 * to make it operational.
651596ba34bSRafael J. Wysocki 	 */
65217b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
653596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
654596ba34bSRafael J. Wysocki 
655b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
656b6c10c84SRafael J. Wysocki 	if (ret) {
657b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
658b6c10c84SRafael J. Wysocki 
659b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
660b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
661b6c10c84SRafael J. Wysocki 
662b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
66317b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
664b6c10c84SRafael J. Wysocki 	}
66517b75ecaSRafael J. Wysocki 
66617b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
667b6c10c84SRafael J. Wysocki 	return ret;
668596ba34bSRafael J. Wysocki }
669596ba34bSRafael J. Wysocki 
670596ba34bSRafael J. Wysocki /**
671596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
672596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
673596ba34bSRafael J. Wysocki  *
674596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
675596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
676596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
677596ba34bSRafael J. Wysocki  */
678596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
679596ba34bSRafael J. Wysocki {
680596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
681596ba34bSRafael J. Wysocki 
682596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
683596ba34bSRafael J. Wysocki 
684596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
685596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
686596ba34bSRafael J. Wysocki 		return -EINVAL;
687596ba34bSRafael J. Wysocki 
688596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
689596ba34bSRafael J. Wysocki }
690596ba34bSRafael J. Wysocki 
691596ba34bSRafael J. Wysocki /**
692596ba34bSRafael J. Wysocki  * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
693596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
694596ba34bSRafael J. Wysocki  *
695596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
696596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
697596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
698596ba34bSRafael J. Wysocki  */
699596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
700596ba34bSRafael J. Wysocki {
701596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
702596ba34bSRafael J. Wysocki 	int ret;
703596ba34bSRafael J. Wysocki 
704596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
705596ba34bSRafael J. Wysocki 
706596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
707596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
708596ba34bSRafael J. Wysocki 		return -EINVAL;
709596ba34bSRafael J. Wysocki 
710596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
711596ba34bSRafael J. Wysocki 		return 0;
712596ba34bSRafael J. Wysocki 
713596ba34bSRafael J. Wysocki 	ret = pm_generic_suspend_noirq(dev);
714596ba34bSRafael J. Wysocki 	if (ret)
715596ba34bSRafael J. Wysocki 		return ret;
716596ba34bSRafael J. Wysocki 
717d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
718d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
719d4f2d87aSRafael J. Wysocki 		return 0;
720d4f2d87aSRafael J. Wysocki 
721596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
722596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
723596ba34bSRafael J. Wysocki 
724596ba34bSRafael J. Wysocki 	/*
725596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
726596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
727596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
728596ba34bSRafael J. Wysocki 	 */
729596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
730596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
731596ba34bSRafael J. Wysocki 
732596ba34bSRafael J. Wysocki 	return 0;
733596ba34bSRafael J. Wysocki }
734596ba34bSRafael J. Wysocki 
735596ba34bSRafael J. Wysocki /**
736596ba34bSRafael J. Wysocki  * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
737596ba34bSRafael J. Wysocki  * @dev: Device to resume.
738596ba34bSRafael J. Wysocki  *
739596ba34bSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
740596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
741596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
742596ba34bSRafael J. Wysocki  * devices.
743596ba34bSRafael J. Wysocki  */
744596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
745596ba34bSRafael J. Wysocki {
746596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
747596ba34bSRafael J. Wysocki 
748596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
749596ba34bSRafael J. Wysocki 
750596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
751596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
752596ba34bSRafael J. Wysocki 		return -EINVAL;
753596ba34bSRafael J. Wysocki 
754596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
755596ba34bSRafael J. Wysocki 		return 0;
756596ba34bSRafael J. Wysocki 
757596ba34bSRafael J. Wysocki 	/*
758596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
759596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
760596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
761596ba34bSRafael J. Wysocki 	 */
762596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
763596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
764596ba34bSRafael J. Wysocki 	if (genpd->start_device)
765596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
766596ba34bSRafael J. Wysocki 
767596ba34bSRafael J. Wysocki 	return pm_generic_resume_noirq(dev);
768596ba34bSRafael J. Wysocki }
769596ba34bSRafael J. Wysocki 
770596ba34bSRafael J. Wysocki /**
771596ba34bSRafael J. Wysocki  * pm_genpd_resume - Resume a device belonging to an I/O power domain.
772596ba34bSRafael J. Wysocki  * @dev: Device to resume.
773596ba34bSRafael J. Wysocki  *
774596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
775596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
776596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
777596ba34bSRafael J. Wysocki  */
778596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
779596ba34bSRafael J. Wysocki {
780596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
781596ba34bSRafael J. Wysocki 
782596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
783596ba34bSRafael J. Wysocki 
784596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
785596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
786596ba34bSRafael J. Wysocki 		return -EINVAL;
787596ba34bSRafael J. Wysocki 
788596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
789596ba34bSRafael J. Wysocki }
790596ba34bSRafael J. Wysocki 
791596ba34bSRafael J. Wysocki /**
792596ba34bSRafael J. Wysocki  * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
793596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
794596ba34bSRafael J. Wysocki  *
795596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
796596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
797596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
798596ba34bSRafael J. Wysocki  */
799596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
800596ba34bSRafael J. Wysocki {
801596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
802596ba34bSRafael J. Wysocki 
803596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
804596ba34bSRafael J. Wysocki 
805596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
806596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
807596ba34bSRafael J. Wysocki 		return -EINVAL;
808596ba34bSRafael J. Wysocki 
809596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
810596ba34bSRafael J. Wysocki }
811596ba34bSRafael J. Wysocki 
812596ba34bSRafael J. Wysocki /**
813596ba34bSRafael J. Wysocki  * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
814596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
815596ba34bSRafael J. Wysocki  *
816596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
817596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
818596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
819596ba34bSRafael J. Wysocki  * devices.
820596ba34bSRafael J. Wysocki  */
821596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
822596ba34bSRafael J. Wysocki {
823596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
824596ba34bSRafael J. Wysocki 	int ret;
825596ba34bSRafael J. Wysocki 
826596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
827596ba34bSRafael J. Wysocki 
828596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
829596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
830596ba34bSRafael J. Wysocki 		return -EINVAL;
831596ba34bSRafael J. Wysocki 
832596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
833596ba34bSRafael J. Wysocki 		return 0;
834596ba34bSRafael J. Wysocki 
835596ba34bSRafael J. Wysocki 	ret = pm_generic_freeze_noirq(dev);
836596ba34bSRafael J. Wysocki 	if (ret)
837596ba34bSRafael J. Wysocki 		return ret;
838596ba34bSRafael J. Wysocki 
839596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
840596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
841596ba34bSRafael J. Wysocki 
842596ba34bSRafael J. Wysocki 	return 0;
843596ba34bSRafael J. Wysocki }
844596ba34bSRafael J. Wysocki 
845596ba34bSRafael J. Wysocki /**
846596ba34bSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
847596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
848596ba34bSRafael J. Wysocki  *
849596ba34bSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
850596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
851596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
852596ba34bSRafael J. Wysocki  * devices.
853596ba34bSRafael J. Wysocki  */
854596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
855596ba34bSRafael J. Wysocki {
856596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
857596ba34bSRafael J. Wysocki 
858596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
859596ba34bSRafael J. Wysocki 
860596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
861596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
862596ba34bSRafael J. Wysocki 		return -EINVAL;
863596ba34bSRafael J. Wysocki 
864596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
865596ba34bSRafael J. Wysocki 		return 0;
866596ba34bSRafael J. Wysocki 
867596ba34bSRafael J. Wysocki 	if (genpd->start_device)
868596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
869596ba34bSRafael J. Wysocki 
870596ba34bSRafael J. Wysocki 	return pm_generic_thaw_noirq(dev);
871596ba34bSRafael J. Wysocki }
872596ba34bSRafael J. Wysocki 
873596ba34bSRafael J. Wysocki /**
874596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
875596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
876596ba34bSRafael J. Wysocki  *
877596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
878596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
879596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
880596ba34bSRafael J. Wysocki  */
881596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
882596ba34bSRafael J. Wysocki {
883596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
884596ba34bSRafael J. Wysocki 
885596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
886596ba34bSRafael J. Wysocki 
887596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
888596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
889596ba34bSRafael J. Wysocki 		return -EINVAL;
890596ba34bSRafael J. Wysocki 
891596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
892596ba34bSRafael J. Wysocki }
893596ba34bSRafael J. Wysocki 
894596ba34bSRafael J. Wysocki /**
895596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
896596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
897596ba34bSRafael J. Wysocki  *
898596ba34bSRafael J. Wysocki  * Power off a device under the assumption that its pm_domain field points to
899596ba34bSRafael J. Wysocki  * the domain member of an object of type struct generic_pm_domain representing
900596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
901596ba34bSRafael J. Wysocki  */
902596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev)
903596ba34bSRafael J. Wysocki {
904596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
905596ba34bSRafael J. Wysocki 
906596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
907596ba34bSRafael J. Wysocki 
908596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
909596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
910596ba34bSRafael J. Wysocki 		return -EINVAL;
911596ba34bSRafael J. Wysocki 
912596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
913596ba34bSRafael J. Wysocki }
914596ba34bSRafael J. Wysocki 
915596ba34bSRafael J. Wysocki /**
916596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
917596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
918596ba34bSRafael J. Wysocki  *
919596ba34bSRafael J. Wysocki  * Carry out a late powering off of a device under the assumption that its
920596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
921596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
922596ba34bSRafael J. Wysocki  */
923596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev)
924596ba34bSRafael J. Wysocki {
925596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
926596ba34bSRafael J. Wysocki 	int ret;
927596ba34bSRafael J. Wysocki 
928596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
929596ba34bSRafael J. Wysocki 
930596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
931596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
932596ba34bSRafael J. Wysocki 		return -EINVAL;
933596ba34bSRafael J. Wysocki 
934596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
935596ba34bSRafael J. Wysocki 		return 0;
936596ba34bSRafael J. Wysocki 
937596ba34bSRafael J. Wysocki 	ret = pm_generic_poweroff_noirq(dev);
938596ba34bSRafael J. Wysocki 	if (ret)
939596ba34bSRafael J. Wysocki 		return ret;
940596ba34bSRafael J. Wysocki 
941d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
942d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
943d4f2d87aSRafael J. Wysocki 		return 0;
944d4f2d87aSRafael J. Wysocki 
945596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
946596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
947596ba34bSRafael J. Wysocki 
948596ba34bSRafael J. Wysocki 	/*
949596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
950596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
951596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
952596ba34bSRafael J. Wysocki 	 */
953596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
954596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
955596ba34bSRafael J. Wysocki 
956596ba34bSRafael J. Wysocki 	return 0;
957596ba34bSRafael J. Wysocki }
958596ba34bSRafael J. Wysocki 
959596ba34bSRafael J. Wysocki /**
960596ba34bSRafael J. Wysocki  * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
961596ba34bSRafael J. Wysocki  * @dev: Device to resume.
962596ba34bSRafael J. Wysocki  *
963596ba34bSRafael J. Wysocki  * Carry out an early restore of a device under the assumption that its
964596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
965596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
966596ba34bSRafael J. Wysocki  * devices.
967596ba34bSRafael J. Wysocki  */
968596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
969596ba34bSRafael J. Wysocki {
970596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
971596ba34bSRafael J. Wysocki 
972596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
973596ba34bSRafael J. Wysocki 
974596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
975596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
976596ba34bSRafael J. Wysocki 		return -EINVAL;
977596ba34bSRafael J. Wysocki 
978596ba34bSRafael J. Wysocki 	/*
979596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
980596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
981596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
982596ba34bSRafael J. Wysocki 	 */
98317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
984596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
985596ba34bSRafael J. Wysocki 		/*
986596ba34bSRafael J. Wysocki 		 * The boot kernel might put the domain into the power on state,
987596ba34bSRafael J. Wysocki 		 * so make sure it really is powered off.
988596ba34bSRafael J. Wysocki 		 */
989596ba34bSRafael J. Wysocki 		if (genpd->power_off)
990596ba34bSRafael J. Wysocki 			genpd->power_off(genpd);
991596ba34bSRafael J. Wysocki 		return 0;
992596ba34bSRafael J. Wysocki 	}
993596ba34bSRafael J. Wysocki 
994596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
995596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
996596ba34bSRafael J. Wysocki 	if (genpd->start_device)
997596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
998596ba34bSRafael J. Wysocki 
999596ba34bSRafael J. Wysocki 	return pm_generic_restore_noirq(dev);
1000596ba34bSRafael J. Wysocki }
1001596ba34bSRafael J. Wysocki 
1002596ba34bSRafael J. Wysocki /**
1003596ba34bSRafael J. Wysocki  * pm_genpd_restore - Restore a device belonging to an I/O power domain.
1004596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1005596ba34bSRafael J. Wysocki  *
1006596ba34bSRafael J. Wysocki  * Restore a device under the assumption that its pm_domain field points to the
1007596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1008596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1009596ba34bSRafael J. Wysocki  */
1010596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev)
1011596ba34bSRafael J. Wysocki {
1012596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1013596ba34bSRafael J. Wysocki 
1014596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1015596ba34bSRafael J. Wysocki 
1016596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1017596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1018596ba34bSRafael J. Wysocki 		return -EINVAL;
1019596ba34bSRafael J. Wysocki 
1020596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
1021596ba34bSRafael J. Wysocki }
1022596ba34bSRafael J. Wysocki 
1023596ba34bSRafael J. Wysocki /**
1024596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1025596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1026596ba34bSRafael J. Wysocki  *
1027596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1028596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1029596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1030596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1031596ba34bSRafael J. Wysocki  */
1032596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1033596ba34bSRafael J. Wysocki {
1034596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1035596ba34bSRafael J. Wysocki 	bool run_complete;
1036596ba34bSRafael J. Wysocki 
1037596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1038596ba34bSRafael J. Wysocki 
1039596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1040596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1041596ba34bSRafael J. Wysocki 		return;
1042596ba34bSRafael J. Wysocki 
1043596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1044596ba34bSRafael J. Wysocki 
1045596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1046596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1047596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1048596ba34bSRafael J. Wysocki 
1049596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1050596ba34bSRafael J. Wysocki 
1051596ba34bSRafael J. Wysocki 	if (run_complete) {
1052596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
10536f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1054596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
10556f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1056596ba34bSRafael J. Wysocki 	}
1057596ba34bSRafael J. Wysocki }
1058596ba34bSRafael J. Wysocki 
1059596ba34bSRafael J. Wysocki #else
1060596ba34bSRafael J. Wysocki 
1061596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1062596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
1063596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
1064596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1065596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1066596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
1067596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
1068596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1069596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1070596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq	NULL
1071596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff		NULL
1072596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1073596ba34bSRafael J. Wysocki #define pm_genpd_restore		NULL
1074596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1075596ba34bSRafael J. Wysocki 
1076596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1077596ba34bSRafael J. Wysocki 
1078f721889fSRafael J. Wysocki /**
1079f721889fSRafael J. Wysocki  * pm_genpd_add_device - Add a device to an I/O PM domain.
1080f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1081f721889fSRafael J. Wysocki  * @dev: Device to be added.
1082f721889fSRafael J. Wysocki  */
1083f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
1084f721889fSRafael J. Wysocki {
1085cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
10864605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1087f721889fSRafael J. Wysocki 	int ret = 0;
1088f721889fSRafael J. Wysocki 
1089f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1090f721889fSRafael J. Wysocki 
1091f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1092f721889fSRafael J. Wysocki 		return -EINVAL;
1093f721889fSRafael J. Wysocki 
109417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1095f721889fSRafael J. Wysocki 
109617b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF) {
1097f721889fSRafael J. Wysocki 		ret = -EINVAL;
1098f721889fSRafael J. Wysocki 		goto out;
1099f721889fSRafael J. Wysocki 	}
1100f721889fSRafael J. Wysocki 
1101596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1102596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1103596ba34bSRafael J. Wysocki 		goto out;
1104596ba34bSRafael J. Wysocki 	}
1105596ba34bSRafael J. Wysocki 
11064605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
11074605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1108f721889fSRafael J. Wysocki 			ret = -EINVAL;
1109f721889fSRafael J. Wysocki 			goto out;
1110f721889fSRafael J. Wysocki 		}
1111f721889fSRafael J. Wysocki 
1112cd0ea672SRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
1113cd0ea672SRafael J. Wysocki 	if (!gpd_data) {
1114cd0ea672SRafael J. Wysocki 		ret = -ENOMEM;
1115cd0ea672SRafael J. Wysocki 		goto out;
1116cd0ea672SRafael J. Wysocki 	}
1117cd0ea672SRafael J. Wysocki 
1118596ba34bSRafael J. Wysocki 	genpd->device_count++;
1119f721889fSRafael J. Wysocki 
1120f721889fSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
11214605ab65SRafael J. Wysocki 	dev_pm_get_subsys_data(dev);
1122cd0ea672SRafael J. Wysocki 	dev->power.subsys_data->domain_data = &gpd_data->base;
1123cd0ea672SRafael J. Wysocki 	gpd_data->base.dev = dev;
1124cd0ea672SRafael J. Wysocki 	gpd_data->need_restore = false;
1125cd0ea672SRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1126f721889fSRafael J. Wysocki 
1127f721889fSRafael J. Wysocki  out:
112817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1129f721889fSRafael J. Wysocki 
1130f721889fSRafael J. Wysocki 	return ret;
1131f721889fSRafael J. Wysocki }
1132f721889fSRafael J. Wysocki 
1133f721889fSRafael J. Wysocki /**
1134f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1135f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1136f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1137f721889fSRafael J. Wysocki  */
1138f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1139f721889fSRafael J. Wysocki 			   struct device *dev)
1140f721889fSRafael J. Wysocki {
11414605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1142f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1143f721889fSRafael J. Wysocki 
1144f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1145f721889fSRafael J. Wysocki 
1146f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1147f721889fSRafael J. Wysocki 		return -EINVAL;
1148f721889fSRafael J. Wysocki 
114917b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1150f721889fSRafael J. Wysocki 
1151596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1152596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1153596ba34bSRafael J. Wysocki 		goto out;
1154596ba34bSRafael J. Wysocki 	}
1155596ba34bSRafael J. Wysocki 
11564605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
11574605ab65SRafael J. Wysocki 		if (pdd->dev != dev)
1158f721889fSRafael J. Wysocki 			continue;
1159f721889fSRafael J. Wysocki 
11604605ab65SRafael J. Wysocki 		list_del_init(&pdd->list_node);
11614605ab65SRafael J. Wysocki 		pdd->dev = NULL;
11624605ab65SRafael J. Wysocki 		dev_pm_put_subsys_data(dev);
1163f721889fSRafael J. Wysocki 		dev->pm_domain = NULL;
1164cd0ea672SRafael J. Wysocki 		kfree(to_gpd_data(pdd));
1165f721889fSRafael J. Wysocki 
1166596ba34bSRafael J. Wysocki 		genpd->device_count--;
1167f721889fSRafael J. Wysocki 
1168f721889fSRafael J. Wysocki 		ret = 0;
1169f721889fSRafael J. Wysocki 		break;
1170f721889fSRafael J. Wysocki 	}
1171f721889fSRafael J. Wysocki 
1172596ba34bSRafael J. Wysocki  out:
117317b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1174f721889fSRafael J. Wysocki 
1175f721889fSRafael J. Wysocki 	return ret;
1176f721889fSRafael J. Wysocki }
1177f721889fSRafael J. Wysocki 
1178f721889fSRafael J. Wysocki /**
1179f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1180f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1181bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1182f721889fSRafael J. Wysocki  */
1183f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1184bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1185f721889fSRafael J. Wysocki {
11865063ce15SRafael J. Wysocki 	struct gpd_link *link;
1187f721889fSRafael J. Wysocki 	int ret = 0;
1188f721889fSRafael J. Wysocki 
1189bc0403ffSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1190f721889fSRafael J. Wysocki 		return -EINVAL;
1191f721889fSRafael J. Wysocki 
119217b75ecaSRafael J. Wysocki  start:
119317b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1194bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1195f721889fSRafael J. Wysocki 
1196bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1197bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1198bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
119917b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
120017b75ecaSRafael J. Wysocki 		goto start;
120117b75ecaSRafael J. Wysocki 	}
120217b75ecaSRafael J. Wysocki 
120317b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1204bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1205f721889fSRafael J. Wysocki 		ret = -EINVAL;
1206f721889fSRafael J. Wysocki 		goto out;
1207f721889fSRafael J. Wysocki 	}
1208f721889fSRafael J. Wysocki 
12095063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1210bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1211f721889fSRafael J. Wysocki 			ret = -EINVAL;
1212f721889fSRafael J. Wysocki 			goto out;
1213f721889fSRafael J. Wysocki 		}
1214f721889fSRafael J. Wysocki 	}
1215f721889fSRafael J. Wysocki 
12165063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
12175063ce15SRafael J. Wysocki 	if (!link) {
12185063ce15SRafael J. Wysocki 		ret = -ENOMEM;
12195063ce15SRafael J. Wysocki 		goto out;
12205063ce15SRafael J. Wysocki 	}
12215063ce15SRafael J. Wysocki 	link->master = genpd;
12225063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1223bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1224bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1225bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1226c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1227f721889fSRafael J. Wysocki 
1228f721889fSRafael J. Wysocki  out:
1229bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
123017b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1231f721889fSRafael J. Wysocki 
1232f721889fSRafael J. Wysocki 	return ret;
1233f721889fSRafael J. Wysocki }
1234f721889fSRafael J. Wysocki 
1235f721889fSRafael J. Wysocki /**
1236f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1237f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
12385063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1239f721889fSRafael J. Wysocki  */
1240f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
12415063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1242f721889fSRafael J. Wysocki {
12435063ce15SRafael J. Wysocki 	struct gpd_link *link;
1244f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1245f721889fSRafael J. Wysocki 
12465063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1247f721889fSRafael J. Wysocki 		return -EINVAL;
1248f721889fSRafael J. Wysocki 
124917b75ecaSRafael J. Wysocki  start:
125017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1251f721889fSRafael J. Wysocki 
12525063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
12535063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1254f721889fSRafael J. Wysocki 			continue;
1255f721889fSRafael J. Wysocki 
1256f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1257f721889fSRafael J. Wysocki 
125817b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
125917b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
126017b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
126117b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
126217b75ecaSRafael J. Wysocki 			goto start;
126317b75ecaSRafael J. Wysocki 		}
126417b75ecaSRafael J. Wysocki 
12655063ce15SRafael J. Wysocki 		list_del(&link->master_node);
12665063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
12675063ce15SRafael J. Wysocki 		kfree(link);
126817b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1269f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1270f721889fSRafael J. Wysocki 
1271f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1272f721889fSRafael J. Wysocki 
1273f721889fSRafael J. Wysocki 		ret = 0;
1274f721889fSRafael J. Wysocki 		break;
1275f721889fSRafael J. Wysocki 	}
1276f721889fSRafael J. Wysocki 
127717b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1278f721889fSRafael J. Wysocki 
1279f721889fSRafael J. Wysocki 	return ret;
1280f721889fSRafael J. Wysocki }
1281f721889fSRafael J. Wysocki 
1282f721889fSRafael J. Wysocki /**
1283f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1284f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1285f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1286f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1287f721889fSRafael J. Wysocki  */
1288f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1289f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1290f721889fSRafael J. Wysocki {
1291f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1292f721889fSRafael J. Wysocki 		return;
1293f721889fSRafael J. Wysocki 
12945063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
12955063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
1296f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1297f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1298f721889fSRafael J. Wysocki 	genpd->gov = gov;
1299f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1300f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1301c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
130217b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
130317b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1304c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1305c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1306596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1307596ba34bSRafael J. Wysocki 	genpd->suspended_count = 0;
1308f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1309f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1310f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1311596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1312596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
1313596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1314596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
1315596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1316596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
1317596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1318596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
1319596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1320596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
1321596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
1322596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
1323596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_restore;
1324596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
13255125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
13265125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
13275125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
13285125bbf3SRafael J. Wysocki }
1329