xref: /openbmc/linux/drivers/base/power/domain.c (revision fe202fde)
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 
32f721889fSRafael J. Wysocki static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
33f721889fSRafael J. Wysocki {
34f721889fSRafael J. Wysocki 	if (!WARN_ON(genpd->sd_count == 0))
35f721889fSRafael J. Wysocki 			genpd->sd_count--;
36f721889fSRafael J. Wysocki }
37f721889fSRafael J. Wysocki 
3817b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
3917b75ecaSRafael J. Wysocki {
4017b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
4117b75ecaSRafael J. Wysocki 
4217b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
4317b75ecaSRafael J. Wysocki 	/*
4417b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
4517b75ecaSRafael J. Wysocki 	 * or the power off state.
4617b75ecaSRafael J. Wysocki 	 */
4717b75ecaSRafael J. Wysocki 	for (;;) {
4817b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
4917b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
50c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
51c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
5217b75ecaSRafael J. Wysocki 			break;
5317b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
5417b75ecaSRafael J. Wysocki 
5517b75ecaSRafael J. Wysocki 		schedule();
5617b75ecaSRafael J. Wysocki 
5717b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
5817b75ecaSRafael J. Wysocki 	}
5917b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
6017b75ecaSRafael J. Wysocki }
6117b75ecaSRafael J. Wysocki 
6217b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
6317b75ecaSRafael J. Wysocki {
6417b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
6517b75ecaSRafael J. Wysocki }
6617b75ecaSRafael J. Wysocki 
67c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
68c6d22b37SRafael J. Wysocki {
69c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
70c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
71c6d22b37SRafael J. Wysocki }
72c6d22b37SRafael J. Wysocki 
73f721889fSRafael J. Wysocki /**
745248051bSRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its parents.
755248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
765248051bSRafael J. Wysocki  *
775248051bSRafael J. Wysocki  * Restore power to @genpd and all of its parents so that it is possible to
785248051bSRafael J. Wysocki  * resume a device belonging to it.
795248051bSRafael J. Wysocki  */
8018b4f3f5SMagnus Damm int pm_genpd_poweron(struct generic_pm_domain *genpd)
815248051bSRafael J. Wysocki {
8217b75ecaSRafael J. Wysocki 	struct generic_pm_domain *parent = genpd->parent;
835248051bSRafael J. Wysocki 	int ret = 0;
845248051bSRafael J. Wysocki 
855248051bSRafael J. Wysocki  start:
8617b75ecaSRafael J. Wysocki 	if (parent) {
87c6d22b37SRafael J. Wysocki 		genpd_acquire_lock(parent);
885248051bSRafael J. Wysocki 		mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
8917b75ecaSRafael J. Wysocki 	} else {
9017b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
9117b75ecaSRafael J. Wysocki 	}
9217b75ecaSRafael J. Wysocki 
9317b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
94596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
955248051bSRafael J. Wysocki 		goto out;
965248051bSRafael J. Wysocki 
97c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
98c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
99c6d22b37SRafael J. Wysocki 		goto out;
100c6d22b37SRafael J. Wysocki 	}
101c6d22b37SRafael J. Wysocki 
10217b75ecaSRafael J. Wysocki 	if (parent && parent->status != GPD_STATE_ACTIVE) {
1035248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
104c6d22b37SRafael J. Wysocki 		genpd_release_lock(parent);
1055248051bSRafael J. Wysocki 
10617b75ecaSRafael J. Wysocki 		ret = pm_genpd_poweron(parent);
1075248051bSRafael J. Wysocki 		if (ret)
1085248051bSRafael J. Wysocki 			return ret;
1095248051bSRafael J. Wysocki 
1105248051bSRafael J. Wysocki 		goto start;
1115248051bSRafael J. Wysocki 	}
1125248051bSRafael J. Wysocki 
1135248051bSRafael J. Wysocki 	if (genpd->power_on) {
114fe202fdeSRafael J. Wysocki 		ret = genpd->power_on(genpd);
1155248051bSRafael J. Wysocki 		if (ret)
1165248051bSRafael J. Wysocki 			goto out;
1175248051bSRafael J. Wysocki 	}
1185248051bSRafael J. Wysocki 
119c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
12017b75ecaSRafael J. Wysocki 	if (parent)
12117b75ecaSRafael J. Wysocki 		parent->sd_count++;
1225248051bSRafael J. Wysocki 
1235248051bSRafael J. Wysocki  out:
1245248051bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
12517b75ecaSRafael J. Wysocki 	if (parent)
126c6d22b37SRafael J. Wysocki 		genpd_release_lock(parent);
1275248051bSRafael J. Wysocki 
1285248051bSRafael J. Wysocki 	return ret;
1295248051bSRafael J. Wysocki }
1305248051bSRafael J. Wysocki 
1315248051bSRafael J. Wysocki #endif /* CONFIG_PM */
1325248051bSRafael J. Wysocki 
1335248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
1345248051bSRafael J. Wysocki 
1355248051bSRafael J. Wysocki /**
136f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
137f721889fSRafael J. Wysocki  * @dle: Device list entry of the device to save the state of.
138f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
139f721889fSRafael J. Wysocki  */
140f721889fSRafael J. Wysocki static int __pm_genpd_save_device(struct dev_list_entry *dle,
141f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
14217b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
143f721889fSRafael J. Wysocki {
144f721889fSRafael J. Wysocki 	struct device *dev = dle->dev;
145f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
146f721889fSRafael J. Wysocki 	int ret = 0;
147f721889fSRafael J. Wysocki 
148f721889fSRafael J. Wysocki 	if (dle->need_restore)
149f721889fSRafael J. Wysocki 		return 0;
150f721889fSRafael J. Wysocki 
15117b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
15217b75ecaSRafael J. Wysocki 
153f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_suspend) {
154f721889fSRafael J. Wysocki 		if (genpd->start_device)
155f721889fSRafael J. Wysocki 			genpd->start_device(dev);
156f721889fSRafael J. Wysocki 
157f721889fSRafael J. Wysocki 		ret = drv->pm->runtime_suspend(dev);
158f721889fSRafael J. Wysocki 
159f721889fSRafael J. Wysocki 		if (genpd->stop_device)
160f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
161f721889fSRafael J. Wysocki 	}
162f721889fSRafael J. Wysocki 
16317b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
16417b75ecaSRafael J. Wysocki 
165f721889fSRafael J. Wysocki 	if (!ret)
166f721889fSRafael J. Wysocki 		dle->need_restore = true;
167f721889fSRafael J. Wysocki 
168f721889fSRafael J. Wysocki 	return ret;
169f721889fSRafael J. Wysocki }
170f721889fSRafael J. Wysocki 
171f721889fSRafael J. Wysocki /**
172f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
173f721889fSRafael J. Wysocki  * @dle: Device list entry of the device to restore the state of.
174f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
175f721889fSRafael J. Wysocki  */
176f721889fSRafael J. Wysocki static void __pm_genpd_restore_device(struct dev_list_entry *dle,
177f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
17817b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
179f721889fSRafael J. Wysocki {
180f721889fSRafael J. Wysocki 	struct device *dev = dle->dev;
181f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
182f721889fSRafael J. Wysocki 
183f721889fSRafael J. Wysocki 	if (!dle->need_restore)
184f721889fSRafael J. Wysocki 		return;
185f721889fSRafael J. Wysocki 
18617b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
18717b75ecaSRafael J. Wysocki 
188f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_resume) {
189f721889fSRafael J. Wysocki 		if (genpd->start_device)
190f721889fSRafael J. Wysocki 			genpd->start_device(dev);
191f721889fSRafael J. Wysocki 
192f721889fSRafael J. Wysocki 		drv->pm->runtime_resume(dev);
193f721889fSRafael J. Wysocki 
194f721889fSRafael J. Wysocki 		if (genpd->stop_device)
195f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
196f721889fSRafael J. Wysocki 	}
197f721889fSRafael J. Wysocki 
19817b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
19917b75ecaSRafael J. Wysocki 
200f721889fSRafael J. Wysocki 	dle->need_restore = false;
201f721889fSRafael J. Wysocki }
202f721889fSRafael J. Wysocki 
203f721889fSRafael J. Wysocki /**
204c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
205c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
206c6d22b37SRafael J. Wysocki  *
207c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
208c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
209c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
210c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
211c6d22b37SRafael J. Wysocki  */
212c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
213c6d22b37SRafael J. Wysocki {
214c6d22b37SRafael J. Wysocki 	return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
215c6d22b37SRafael J. Wysocki }
216c6d22b37SRafael J. Wysocki 
217c6d22b37SRafael J. Wysocki /**
21856375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
21956375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
22056375fd4SRafael J. Wysocki  *
22156375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
22256375fd4SRafael J. Wysocki  * before.
22356375fd4SRafael J. Wysocki  */
2240bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
22556375fd4SRafael J. Wysocki {
22656375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
22756375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
22856375fd4SRafael J. Wysocki }
22956375fd4SRafael J. Wysocki 
23056375fd4SRafael J. Wysocki /**
231f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
232f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
233f721889fSRafael J. Wysocki  *
234f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
235f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
236f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
237f721889fSRafael J. Wysocki  */
238f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
23917b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
240f721889fSRafael J. Wysocki {
241f721889fSRafael J. Wysocki 	struct generic_pm_domain *parent;
242f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
243f721889fSRafael J. Wysocki 	unsigned int not_suspended;
244c6d22b37SRafael J. Wysocki 	int ret = 0;
245f721889fSRafael J. Wysocki 
246c6d22b37SRafael J. Wysocki  start:
247c6d22b37SRafael J. Wysocki 	/*
248c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
249c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
250c6d22b37SRafael J. Wysocki 	 * (2) System suspend is in progress.
251c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
252c6d22b37SRafael J. Wysocki 	 */
253c6d22b37SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0
254c6d22b37SRafael J. Wysocki 	    || genpd->resume_count > 0)
255f721889fSRafael J. Wysocki 		return 0;
256f721889fSRafael J. Wysocki 
257f721889fSRafael J. Wysocki 	if (genpd->sd_count > 0)
258f721889fSRafael J. Wysocki 		return -EBUSY;
259f721889fSRafael J. Wysocki 
260f721889fSRafael J. Wysocki 	not_suspended = 0;
261f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node)
262f721889fSRafael J. Wysocki 		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
263f721889fSRafael J. Wysocki 			not_suspended++;
264f721889fSRafael J. Wysocki 
265f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
266f721889fSRafael J. Wysocki 		return -EBUSY;
267f721889fSRafael J. Wysocki 
268c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
269c6d22b37SRafael J. Wysocki 		/*
270c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
271c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
272c6d22b37SRafael J. Wysocki 		 */
273c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
274c6d22b37SRafael J. Wysocki 		return 0;
275c6d22b37SRafael J. Wysocki 	}
276c6d22b37SRafael J. Wysocki 
277f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
278f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
279f721889fSRafael J. Wysocki 			return -EAGAIN;
280f721889fSRafael J. Wysocki 	}
281f721889fSRafael J. Wysocki 
28217b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
283c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
28417b75ecaSRafael J. Wysocki 
285f721889fSRafael J. Wysocki 	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
286f721889fSRafael J. Wysocki 		ret = __pm_genpd_save_device(dle, genpd);
287697a7f37SRafael J. Wysocki 		if (ret) {
288697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
289697a7f37SRafael J. Wysocki 			goto out;
290697a7f37SRafael J. Wysocki 		}
291f721889fSRafael J. Wysocki 
292c6d22b37SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
293c6d22b37SRafael J. Wysocki 			goto out;
294c6d22b37SRafael J. Wysocki 
295c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
296c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
297c6d22b37SRafael J. Wysocki 			goto start;
298c6d22b37SRafael J. Wysocki 		}
299c6d22b37SRafael J. Wysocki 	}
30017b75ecaSRafael J. Wysocki 
30117b75ecaSRafael J. Wysocki 	parent = genpd->parent;
30217b75ecaSRafael J. Wysocki 	if (parent) {
303c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
304c6d22b37SRafael J. Wysocki 
30517b75ecaSRafael J. Wysocki 		genpd_acquire_lock(parent);
30617b75ecaSRafael J. Wysocki 		mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
307c6d22b37SRafael J. Wysocki 
308c6d22b37SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd)) {
309c6d22b37SRafael J. Wysocki 			genpd_release_lock(parent);
310c6d22b37SRafael J. Wysocki 			goto out;
311c6d22b37SRafael J. Wysocki 		}
31217b75ecaSRafael J. Wysocki 	}
31317b75ecaSRafael J. Wysocki 
314d2805402SRafael J. Wysocki 	if (genpd->power_off) {
315d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
316d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
317d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
318d2805402SRafael J. Wysocki 			if (parent)
319d2805402SRafael J. Wysocki 				genpd_release_lock(parent);
320d2805402SRafael J. Wysocki 
321d2805402SRafael J. Wysocki 			goto out;
322d2805402SRafael J. Wysocki 		}
323d2805402SRafael J. Wysocki 	}
324f721889fSRafael J. Wysocki 
32517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
326f721889fSRafael J. Wysocki 
327f721889fSRafael J. Wysocki 	if (parent) {
328f721889fSRafael J. Wysocki 		genpd_sd_counter_dec(parent);
329f721889fSRafael J. Wysocki 		if (parent->sd_count == 0)
33056375fd4SRafael J. Wysocki 			genpd_queue_power_off_work(parent);
33117b75ecaSRafael J. Wysocki 
33217b75ecaSRafael J. Wysocki 		genpd_release_lock(parent);
333f721889fSRafael J. Wysocki 	}
334f721889fSRafael J. Wysocki 
335c6d22b37SRafael J. Wysocki  out:
336c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
337c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
338c6d22b37SRafael J. Wysocki 	return ret;
339f721889fSRafael J. Wysocki }
340f721889fSRafael J. Wysocki 
341f721889fSRafael J. Wysocki /**
342f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
343f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
344f721889fSRafael J. Wysocki  */
345f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
346f721889fSRafael J. Wysocki {
347f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
348f721889fSRafael J. Wysocki 
349f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
350f721889fSRafael J. Wysocki 
35117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
352f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
35317b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
354f721889fSRafael J. Wysocki }
355f721889fSRafael J. Wysocki 
356f721889fSRafael J. Wysocki /**
357f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
358f721889fSRafael J. Wysocki  * @dev: Device to suspend.
359f721889fSRafael J. Wysocki  *
360f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
361f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
362f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
363f721889fSRafael J. Wysocki  */
364f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
365f721889fSRafael J. Wysocki {
366f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
367f721889fSRafael J. Wysocki 
368f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
369f721889fSRafael J. Wysocki 
3705248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
3715248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
372f721889fSRafael J. Wysocki 		return -EINVAL;
373f721889fSRafael J. Wysocki 
374f721889fSRafael J. Wysocki 	if (genpd->stop_device) {
375f721889fSRafael J. Wysocki 		int ret = genpd->stop_device(dev);
376f721889fSRafael J. Wysocki 		if (ret)
37717b75ecaSRafael J. Wysocki 			return ret;
378f721889fSRafael J. Wysocki 	}
37917b75ecaSRafael J. Wysocki 
380c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
381f721889fSRafael J. Wysocki 	genpd->in_progress++;
382f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
383f721889fSRafael J. Wysocki 	genpd->in_progress--;
384c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
385f721889fSRafael J. Wysocki 
386f721889fSRafael J. Wysocki 	return 0;
387f721889fSRafael J. Wysocki }
388f721889fSRafael J. Wysocki 
389f721889fSRafael J. Wysocki /**
390596ba34bSRafael J. Wysocki  * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
391596ba34bSRafael J. Wysocki  * @dev: Device to resume.
392596ba34bSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
393596ba34bSRafael J. Wysocki  */
394596ba34bSRafael J. Wysocki static void __pm_genpd_runtime_resume(struct device *dev,
395596ba34bSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
396596ba34bSRafael J. Wysocki {
397596ba34bSRafael J. Wysocki 	struct dev_list_entry *dle;
398596ba34bSRafael J. Wysocki 
399596ba34bSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node) {
400596ba34bSRafael J. Wysocki 		if (dle->dev == dev) {
401596ba34bSRafael J. Wysocki 			__pm_genpd_restore_device(dle, genpd);
402596ba34bSRafael J. Wysocki 			break;
403596ba34bSRafael J. Wysocki 		}
404596ba34bSRafael J. Wysocki 	}
405596ba34bSRafael J. Wysocki }
406596ba34bSRafael J. Wysocki 
407596ba34bSRafael J. Wysocki /**
408f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
409f721889fSRafael J. Wysocki  * @dev: Device to resume.
410f721889fSRafael J. Wysocki  *
411f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
412f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
413f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
414f721889fSRafael J. Wysocki  */
415f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
416f721889fSRafael J. Wysocki {
417f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
418c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
419f721889fSRafael J. Wysocki 	int ret;
420f721889fSRafael J. Wysocki 
421f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
422f721889fSRafael J. Wysocki 
4235248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
4245248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
425f721889fSRafael J. Wysocki 		return -EINVAL;
426f721889fSRafael J. Wysocki 
427f721889fSRafael J. Wysocki 	ret = pm_genpd_poweron(genpd);
428f721889fSRafael J. Wysocki 	if (ret)
429f721889fSRafael J. Wysocki 		return ret;
430f721889fSRafael J. Wysocki 
431c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
43217b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
433c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
434c6d22b37SRafael J. Wysocki 	for (;;) {
435c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
436c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
437c6d22b37SRafael J. Wysocki 		/*
438c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
439c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
440c6d22b37SRafael J. Wysocki 		 * not wait.
441c6d22b37SRafael J. Wysocki 		 */
442c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
443c6d22b37SRafael J. Wysocki 			break;
444c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
445c6d22b37SRafael J. Wysocki 
446c6d22b37SRafael J. Wysocki 		schedule();
447c6d22b37SRafael J. Wysocki 
448c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
449c6d22b37SRafael J. Wysocki 	}
450c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
451596ba34bSRafael J. Wysocki 	__pm_genpd_runtime_resume(dev, genpd);
452c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
453c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
45417b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
455c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
45617b75ecaSRafael J. Wysocki 
45717b75ecaSRafael J. Wysocki 	if (genpd->start_device)
45817b75ecaSRafael J. Wysocki 		genpd->start_device(dev);
459f721889fSRafael J. Wysocki 
460f721889fSRafael J. Wysocki 	return 0;
461f721889fSRafael J. Wysocki }
462f721889fSRafael J. Wysocki 
463f721889fSRafael J. Wysocki #else
464f721889fSRafael J. Wysocki 
465f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
466596ba34bSRafael J. Wysocki static inline void __pm_genpd_runtime_resume(struct device *dev,
467596ba34bSRafael J. Wysocki 					     struct generic_pm_domain *genpd) {}
468f721889fSRafael J. Wysocki 
469f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
470f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
471f721889fSRafael J. Wysocki 
472f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
473f721889fSRafael J. Wysocki 
474596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
475596ba34bSRafael J. Wysocki 
476596ba34bSRafael J. Wysocki /**
477596ba34bSRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
478596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
479596ba34bSRafael J. Wysocki  *
480596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
481596ba34bSRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its parent.
482596ba34bSRafael J. Wysocki  *
483596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
484596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
485596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
486596ba34bSRafael J. Wysocki  */
487596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
488596ba34bSRafael J. Wysocki {
489596ba34bSRafael J. Wysocki 	struct generic_pm_domain *parent = genpd->parent;
490596ba34bSRafael J. Wysocki 
49117b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
492596ba34bSRafael J. Wysocki 		return;
493596ba34bSRafael J. Wysocki 
494596ba34bSRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
495596ba34bSRafael J. Wysocki 		return;
496596ba34bSRafael J. Wysocki 
497596ba34bSRafael J. Wysocki 	if (genpd->power_off)
498596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
499596ba34bSRafael J. Wysocki 
50017b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
501596ba34bSRafael J. Wysocki 	if (parent) {
502596ba34bSRafael J. Wysocki 		genpd_sd_counter_dec(parent);
503596ba34bSRafael J. Wysocki 		pm_genpd_sync_poweroff(parent);
504596ba34bSRafael J. Wysocki 	}
505596ba34bSRafael J. Wysocki }
506596ba34bSRafael J. Wysocki 
507596ba34bSRafael J. Wysocki /**
5084ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
5094ecd6e65SRafael J. Wysocki  * @dev: Device to check.
5104ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
5114ecd6e65SRafael J. Wysocki  *
5124ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
5134ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
5144ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
5154ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
5164ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
5174ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
5184ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
5194ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
5204ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
5214ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
5224ecd6e65SRafael J. Wysocki  */
5234ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
5244ecd6e65SRafael J. Wysocki {
5254ecd6e65SRafael J. Wysocki 	bool active_wakeup;
5264ecd6e65SRafael J. Wysocki 
5274ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
5284ecd6e65SRafael J. Wysocki 		return false;
5294ecd6e65SRafael J. Wysocki 
5304ecd6e65SRafael J. Wysocki 	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
5314ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
5324ecd6e65SRafael J. Wysocki }
5334ecd6e65SRafael J. Wysocki 
5344ecd6e65SRafael J. Wysocki /**
535596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
536596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
537596ba34bSRafael J. Wysocki  *
538596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
539596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
540596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
541596ba34bSRafael J. Wysocki  * consisting of I/O devices.
542596ba34bSRafael J. Wysocki  */
543596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
544596ba34bSRafael J. Wysocki {
545596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
546b6c10c84SRafael J. Wysocki 	int ret;
547596ba34bSRafael J. Wysocki 
548596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
549596ba34bSRafael J. Wysocki 
550596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
551596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
552596ba34bSRafael J. Wysocki 		return -EINVAL;
553596ba34bSRafael J. Wysocki 
55417b75ecaSRafael J. Wysocki 	/*
55517b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
55617b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
55717b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
55817b75ecaSRafael J. Wysocki 	 */
55917b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
56017b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
56117b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
56217b75ecaSRafael J. Wysocki 
56317b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
56417b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
56517b75ecaSRafael J. Wysocki 		return -EBUSY;
56617b75ecaSRafael J. Wysocki 	}
56717b75ecaSRafael J. Wysocki 
5684ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
5694ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
5704ecd6e65SRafael J. Wysocki 
57117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
572596ba34bSRafael J. Wysocki 
573596ba34bSRafael J. Wysocki 	if (genpd->prepared_count++ == 0)
57417b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
57517b75ecaSRafael J. Wysocki 
57617b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
577596ba34bSRafael J. Wysocki 
578596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
57917b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
580596ba34bSRafael J. Wysocki 		return 0;
581596ba34bSRafael J. Wysocki 	}
582596ba34bSRafael J. Wysocki 
583596ba34bSRafael J. Wysocki 	/*
58417b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
58517b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
58617b75ecaSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by .stop_device()), we need
58717b75ecaSRafael J. Wysocki 	 * to make it operational.
588596ba34bSRafael J. Wysocki 	 */
58917b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
590596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
591596ba34bSRafael J. Wysocki 
592b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
593b6c10c84SRafael J. Wysocki 	if (ret) {
594b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
595b6c10c84SRafael J. Wysocki 
596b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
597b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
598b6c10c84SRafael J. Wysocki 
599b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
60017b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
601b6c10c84SRafael J. Wysocki 	}
60217b75ecaSRafael J. Wysocki 
60317b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
604b6c10c84SRafael J. Wysocki 	return ret;
605596ba34bSRafael J. Wysocki }
606596ba34bSRafael J. Wysocki 
607596ba34bSRafael J. Wysocki /**
608596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
609596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
610596ba34bSRafael J. Wysocki  *
611596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
612596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
613596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
614596ba34bSRafael J. Wysocki  */
615596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
616596ba34bSRafael J. Wysocki {
617596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
618596ba34bSRafael J. Wysocki 
619596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
620596ba34bSRafael J. Wysocki 
621596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
622596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
623596ba34bSRafael J. Wysocki 		return -EINVAL;
624596ba34bSRafael J. Wysocki 
625596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
626596ba34bSRafael J. Wysocki }
627596ba34bSRafael J. Wysocki 
628596ba34bSRafael J. Wysocki /**
629596ba34bSRafael J. Wysocki  * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
630596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
631596ba34bSRafael J. Wysocki  *
632596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
633596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
634596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
635596ba34bSRafael J. Wysocki  */
636596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
637596ba34bSRafael J. Wysocki {
638596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
639596ba34bSRafael J. Wysocki 	int ret;
640596ba34bSRafael J. Wysocki 
641596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
642596ba34bSRafael J. Wysocki 
643596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
644596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
645596ba34bSRafael J. Wysocki 		return -EINVAL;
646596ba34bSRafael J. Wysocki 
647596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
648596ba34bSRafael J. Wysocki 		return 0;
649596ba34bSRafael J. Wysocki 
650596ba34bSRafael J. Wysocki 	ret = pm_generic_suspend_noirq(dev);
651596ba34bSRafael J. Wysocki 	if (ret)
652596ba34bSRafael J. Wysocki 		return ret;
653596ba34bSRafael J. Wysocki 
654d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
655d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
656d4f2d87aSRafael J. Wysocki 		return 0;
657d4f2d87aSRafael J. Wysocki 
658596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
659596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
660596ba34bSRafael J. Wysocki 
661596ba34bSRafael J. Wysocki 	/*
662596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
663596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
664596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
665596ba34bSRafael J. Wysocki 	 */
666596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
667596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
668596ba34bSRafael J. Wysocki 
669596ba34bSRafael J. Wysocki 	return 0;
670596ba34bSRafael J. Wysocki }
671596ba34bSRafael J. Wysocki 
672596ba34bSRafael J. Wysocki /**
673596ba34bSRafael J. Wysocki  * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
674596ba34bSRafael J. Wysocki  * @dev: Device to resume.
675596ba34bSRafael J. Wysocki  *
676596ba34bSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
677596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
678596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
679596ba34bSRafael J. Wysocki  * devices.
680596ba34bSRafael J. Wysocki  */
681596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
682596ba34bSRafael J. Wysocki {
683596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
684596ba34bSRafael J. Wysocki 
685596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
686596ba34bSRafael J. Wysocki 
687596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
688596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
689596ba34bSRafael J. Wysocki 		return -EINVAL;
690596ba34bSRafael J. Wysocki 
691596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
692596ba34bSRafael J. Wysocki 		return 0;
693596ba34bSRafael J. Wysocki 
694596ba34bSRafael J. Wysocki 	/*
695596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
696596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
697596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
698596ba34bSRafael J. Wysocki 	 */
699596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
700596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
701596ba34bSRafael J. Wysocki 	if (genpd->start_device)
702596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
703596ba34bSRafael J. Wysocki 
704596ba34bSRafael J. Wysocki 	return pm_generic_resume_noirq(dev);
705596ba34bSRafael J. Wysocki }
706596ba34bSRafael J. Wysocki 
707596ba34bSRafael J. Wysocki /**
708596ba34bSRafael J. Wysocki  * pm_genpd_resume - Resume a device belonging to an I/O power domain.
709596ba34bSRafael J. Wysocki  * @dev: Device to resume.
710596ba34bSRafael J. Wysocki  *
711596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
712596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
713596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
714596ba34bSRafael J. Wysocki  */
715596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
716596ba34bSRafael J. Wysocki {
717596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
718596ba34bSRafael J. Wysocki 
719596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
720596ba34bSRafael J. Wysocki 
721596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
722596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
723596ba34bSRafael J. Wysocki 		return -EINVAL;
724596ba34bSRafael J. Wysocki 
725596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
726596ba34bSRafael J. Wysocki }
727596ba34bSRafael J. Wysocki 
728596ba34bSRafael J. Wysocki /**
729596ba34bSRafael J. Wysocki  * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
730596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
731596ba34bSRafael J. Wysocki  *
732596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
733596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
734596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
735596ba34bSRafael J. Wysocki  */
736596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
737596ba34bSRafael J. Wysocki {
738596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
739596ba34bSRafael J. Wysocki 
740596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
741596ba34bSRafael J. Wysocki 
742596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
743596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
744596ba34bSRafael J. Wysocki 		return -EINVAL;
745596ba34bSRafael J. Wysocki 
746596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
747596ba34bSRafael J. Wysocki }
748596ba34bSRafael J. Wysocki 
749596ba34bSRafael J. Wysocki /**
750596ba34bSRafael J. Wysocki  * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
751596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
752596ba34bSRafael J. Wysocki  *
753596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
754596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
755596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
756596ba34bSRafael J. Wysocki  * devices.
757596ba34bSRafael J. Wysocki  */
758596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
759596ba34bSRafael J. Wysocki {
760596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
761596ba34bSRafael J. Wysocki 	int ret;
762596ba34bSRafael J. Wysocki 
763596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
764596ba34bSRafael J. Wysocki 
765596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
766596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
767596ba34bSRafael J. Wysocki 		return -EINVAL;
768596ba34bSRafael J. Wysocki 
769596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
770596ba34bSRafael J. Wysocki 		return 0;
771596ba34bSRafael J. Wysocki 
772596ba34bSRafael J. Wysocki 	ret = pm_generic_freeze_noirq(dev);
773596ba34bSRafael J. Wysocki 	if (ret)
774596ba34bSRafael J. Wysocki 		return ret;
775596ba34bSRafael J. Wysocki 
776596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
777596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
778596ba34bSRafael J. Wysocki 
779596ba34bSRafael J. Wysocki 	return 0;
780596ba34bSRafael J. Wysocki }
781596ba34bSRafael J. Wysocki 
782596ba34bSRafael J. Wysocki /**
783596ba34bSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
784596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
785596ba34bSRafael J. Wysocki  *
786596ba34bSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
787596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
788596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
789596ba34bSRafael J. Wysocki  * devices.
790596ba34bSRafael J. Wysocki  */
791596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
792596ba34bSRafael J. Wysocki {
793596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
794596ba34bSRafael J. Wysocki 
795596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
796596ba34bSRafael J. Wysocki 
797596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
798596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
799596ba34bSRafael J. Wysocki 		return -EINVAL;
800596ba34bSRafael J. Wysocki 
801596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
802596ba34bSRafael J. Wysocki 		return 0;
803596ba34bSRafael J. Wysocki 
804596ba34bSRafael J. Wysocki 	if (genpd->start_device)
805596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
806596ba34bSRafael J. Wysocki 
807596ba34bSRafael J. Wysocki 	return pm_generic_thaw_noirq(dev);
808596ba34bSRafael J. Wysocki }
809596ba34bSRafael J. Wysocki 
810596ba34bSRafael J. Wysocki /**
811596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
812596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
813596ba34bSRafael J. Wysocki  *
814596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
815596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
816596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
817596ba34bSRafael J. Wysocki  */
818596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
819596ba34bSRafael J. Wysocki {
820596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
821596ba34bSRafael J. Wysocki 
822596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
823596ba34bSRafael J. Wysocki 
824596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
825596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
826596ba34bSRafael J. Wysocki 		return -EINVAL;
827596ba34bSRafael J. Wysocki 
828596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
829596ba34bSRafael J. Wysocki }
830596ba34bSRafael J. Wysocki 
831596ba34bSRafael J. Wysocki /**
832596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
833596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
834596ba34bSRafael J. Wysocki  *
835596ba34bSRafael J. Wysocki  * Power off a device under the assumption that its pm_domain field points to
836596ba34bSRafael J. Wysocki  * the domain member of an object of type struct generic_pm_domain representing
837596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
838596ba34bSRafael J. Wysocki  */
839596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev)
840596ba34bSRafael J. Wysocki {
841596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
842596ba34bSRafael J. Wysocki 
843596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
844596ba34bSRafael J. Wysocki 
845596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
846596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
847596ba34bSRafael J. Wysocki 		return -EINVAL;
848596ba34bSRafael J. Wysocki 
849596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
850596ba34bSRafael J. Wysocki }
851596ba34bSRafael J. Wysocki 
852596ba34bSRafael J. Wysocki /**
853596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
854596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
855596ba34bSRafael J. Wysocki  *
856596ba34bSRafael J. Wysocki  * Carry out a late powering off of a device under the assumption that its
857596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
858596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
859596ba34bSRafael J. Wysocki  */
860596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev)
861596ba34bSRafael J. Wysocki {
862596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
863596ba34bSRafael J. Wysocki 	int ret;
864596ba34bSRafael J. Wysocki 
865596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
866596ba34bSRafael J. Wysocki 
867596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
868596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
869596ba34bSRafael J. Wysocki 		return -EINVAL;
870596ba34bSRafael J. Wysocki 
871596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
872596ba34bSRafael J. Wysocki 		return 0;
873596ba34bSRafael J. Wysocki 
874596ba34bSRafael J. Wysocki 	ret = pm_generic_poweroff_noirq(dev);
875596ba34bSRafael J. Wysocki 	if (ret)
876596ba34bSRafael J. Wysocki 		return ret;
877596ba34bSRafael J. Wysocki 
878d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
879d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
880d4f2d87aSRafael J. Wysocki 		return 0;
881d4f2d87aSRafael J. Wysocki 
882596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
883596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
884596ba34bSRafael J. Wysocki 
885596ba34bSRafael J. Wysocki 	/*
886596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
887596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
888596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
889596ba34bSRafael J. Wysocki 	 */
890596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
891596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
892596ba34bSRafael J. Wysocki 
893596ba34bSRafael J. Wysocki 	return 0;
894596ba34bSRafael J. Wysocki }
895596ba34bSRafael J. Wysocki 
896596ba34bSRafael J. Wysocki /**
897596ba34bSRafael J. Wysocki  * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
898596ba34bSRafael J. Wysocki  * @dev: Device to resume.
899596ba34bSRafael J. Wysocki  *
900596ba34bSRafael J. Wysocki  * Carry out an early restore of a device under the assumption that its
901596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
902596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
903596ba34bSRafael J. Wysocki  * devices.
904596ba34bSRafael J. Wysocki  */
905596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
906596ba34bSRafael J. Wysocki {
907596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
908596ba34bSRafael J. Wysocki 
909596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
910596ba34bSRafael J. Wysocki 
911596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
912596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
913596ba34bSRafael J. Wysocki 		return -EINVAL;
914596ba34bSRafael J. Wysocki 
915596ba34bSRafael J. Wysocki 	/*
916596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
917596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
918596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
919596ba34bSRafael J. Wysocki 	 */
92017b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
921596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
922596ba34bSRafael J. Wysocki 		/*
923596ba34bSRafael J. Wysocki 		 * The boot kernel might put the domain into the power on state,
924596ba34bSRafael J. Wysocki 		 * so make sure it really is powered off.
925596ba34bSRafael J. Wysocki 		 */
926596ba34bSRafael J. Wysocki 		if (genpd->power_off)
927596ba34bSRafael J. Wysocki 			genpd->power_off(genpd);
928596ba34bSRafael J. Wysocki 		return 0;
929596ba34bSRafael J. Wysocki 	}
930596ba34bSRafael J. Wysocki 
931596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
932596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
933596ba34bSRafael J. Wysocki 	if (genpd->start_device)
934596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
935596ba34bSRafael J. Wysocki 
936596ba34bSRafael J. Wysocki 	return pm_generic_restore_noirq(dev);
937596ba34bSRafael J. Wysocki }
938596ba34bSRafael J. Wysocki 
939596ba34bSRafael J. Wysocki /**
940596ba34bSRafael J. Wysocki  * pm_genpd_restore - Restore a device belonging to an I/O power domain.
941596ba34bSRafael J. Wysocki  * @dev: Device to resume.
942596ba34bSRafael J. Wysocki  *
943596ba34bSRafael J. Wysocki  * Restore a device under the assumption that its pm_domain field points to the
944596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
945596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
946596ba34bSRafael J. Wysocki  */
947596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev)
948596ba34bSRafael J. Wysocki {
949596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
950596ba34bSRafael J. Wysocki 
951596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
952596ba34bSRafael J. Wysocki 
953596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
954596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
955596ba34bSRafael J. Wysocki 		return -EINVAL;
956596ba34bSRafael J. Wysocki 
957596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
958596ba34bSRafael J. Wysocki }
959596ba34bSRafael J. Wysocki 
960596ba34bSRafael J. Wysocki /**
961596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
962596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
963596ba34bSRafael J. Wysocki  *
964596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
965596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
966596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
967596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
968596ba34bSRafael J. Wysocki  */
969596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
970596ba34bSRafael J. Wysocki {
971596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
972596ba34bSRafael J. Wysocki 	bool run_complete;
973596ba34bSRafael J. Wysocki 
974596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
975596ba34bSRafael J. Wysocki 
976596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
977596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
978596ba34bSRafael J. Wysocki 		return;
979596ba34bSRafael J. Wysocki 
980596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
981596ba34bSRafael J. Wysocki 
982596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
983596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
984596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
985596ba34bSRafael J. Wysocki 
986596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
987596ba34bSRafael J. Wysocki 
988596ba34bSRafael J. Wysocki 	if (run_complete) {
989596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
9906f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
991596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
9926f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
993596ba34bSRafael J. Wysocki 	}
994596ba34bSRafael J. Wysocki }
995596ba34bSRafael J. Wysocki 
996596ba34bSRafael J. Wysocki #else
997596ba34bSRafael J. Wysocki 
998596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
999596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
1000596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
1001596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1002596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1003596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
1004596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
1005596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1006596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1007596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq	NULL
1008596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff		NULL
1009596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1010596ba34bSRafael J. Wysocki #define pm_genpd_restore		NULL
1011596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1012596ba34bSRafael J. Wysocki 
1013596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1014596ba34bSRafael J. Wysocki 
1015f721889fSRafael J. Wysocki /**
1016f721889fSRafael J. Wysocki  * pm_genpd_add_device - Add a device to an I/O PM domain.
1017f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1018f721889fSRafael J. Wysocki  * @dev: Device to be added.
1019f721889fSRafael J. Wysocki  */
1020f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
1021f721889fSRafael J. Wysocki {
1022f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
1023f721889fSRafael J. Wysocki 	int ret = 0;
1024f721889fSRafael J. Wysocki 
1025f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1026f721889fSRafael J. Wysocki 
1027f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1028f721889fSRafael J. Wysocki 		return -EINVAL;
1029f721889fSRafael J. Wysocki 
103017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1031f721889fSRafael J. Wysocki 
103217b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF) {
1033f721889fSRafael J. Wysocki 		ret = -EINVAL;
1034f721889fSRafael J. Wysocki 		goto out;
1035f721889fSRafael J. Wysocki 	}
1036f721889fSRafael J. Wysocki 
1037596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1038596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1039596ba34bSRafael J. Wysocki 		goto out;
1040596ba34bSRafael J. Wysocki 	}
1041596ba34bSRafael J. Wysocki 
1042f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node)
1043f721889fSRafael J. Wysocki 		if (dle->dev == dev) {
1044f721889fSRafael J. Wysocki 			ret = -EINVAL;
1045f721889fSRafael J. Wysocki 			goto out;
1046f721889fSRafael J. Wysocki 		}
1047f721889fSRafael J. Wysocki 
1048f721889fSRafael J. Wysocki 	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
1049f721889fSRafael J. Wysocki 	if (!dle) {
1050f721889fSRafael J. Wysocki 		ret = -ENOMEM;
1051f721889fSRafael J. Wysocki 		goto out;
1052f721889fSRafael J. Wysocki 	}
1053f721889fSRafael J. Wysocki 
1054f721889fSRafael J. Wysocki 	dle->dev = dev;
1055f721889fSRafael J. Wysocki 	dle->need_restore = false;
1056f721889fSRafael J. Wysocki 	list_add_tail(&dle->node, &genpd->dev_list);
1057596ba34bSRafael J. Wysocki 	genpd->device_count++;
1058f721889fSRafael J. Wysocki 
1059f721889fSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1060f721889fSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
1061f721889fSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1062f721889fSRafael J. Wysocki 
1063f721889fSRafael J. Wysocki  out:
106417b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1065f721889fSRafael J. Wysocki 
1066f721889fSRafael J. Wysocki 	return ret;
1067f721889fSRafael J. Wysocki }
1068f721889fSRafael J. Wysocki 
1069f721889fSRafael J. Wysocki /**
1070f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1071f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1072f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1073f721889fSRafael J. Wysocki  */
1074f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1075f721889fSRafael J. Wysocki 			   struct device *dev)
1076f721889fSRafael J. Wysocki {
1077f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
1078f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1079f721889fSRafael J. Wysocki 
1080f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1081f721889fSRafael J. Wysocki 
1082f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1083f721889fSRafael J. Wysocki 		return -EINVAL;
1084f721889fSRafael J. Wysocki 
108517b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1086f721889fSRafael J. Wysocki 
1087596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1088596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1089596ba34bSRafael J. Wysocki 		goto out;
1090596ba34bSRafael J. Wysocki 	}
1091596ba34bSRafael J. Wysocki 
1092f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node) {
1093f721889fSRafael J. Wysocki 		if (dle->dev != dev)
1094f721889fSRafael J. Wysocki 			continue;
1095f721889fSRafael J. Wysocki 
1096f721889fSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
1097f721889fSRafael J. Wysocki 		dev->pm_domain = NULL;
1098f721889fSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
1099f721889fSRafael J. Wysocki 
1100596ba34bSRafael J. Wysocki 		genpd->device_count--;
1101f721889fSRafael J. Wysocki 		list_del(&dle->node);
1102f721889fSRafael J. Wysocki 		kfree(dle);
1103f721889fSRafael J. Wysocki 
1104f721889fSRafael J. Wysocki 		ret = 0;
1105f721889fSRafael J. Wysocki 		break;
1106f721889fSRafael J. Wysocki 	}
1107f721889fSRafael J. Wysocki 
1108596ba34bSRafael J. Wysocki  out:
110917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1110f721889fSRafael J. Wysocki 
1111f721889fSRafael J. Wysocki 	return ret;
1112f721889fSRafael J. Wysocki }
1113f721889fSRafael J. Wysocki 
1114f721889fSRafael J. Wysocki /**
1115f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1116f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1117f721889fSRafael J. Wysocki  * @new_subdomain: Subdomain to be added.
1118f721889fSRafael J. Wysocki  */
1119f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1120f721889fSRafael J. Wysocki 			   struct generic_pm_domain *new_subdomain)
1121f721889fSRafael J. Wysocki {
1122f721889fSRafael J. Wysocki 	struct generic_pm_domain *subdomain;
1123f721889fSRafael J. Wysocki 	int ret = 0;
1124f721889fSRafael J. Wysocki 
1125f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
1126f721889fSRafael J. Wysocki 		return -EINVAL;
1127f721889fSRafael J. Wysocki 
112817b75ecaSRafael J. Wysocki  start:
112917b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
113017b75ecaSRafael J. Wysocki 	mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
1131f721889fSRafael J. Wysocki 
113217b75ecaSRafael J. Wysocki 	if (new_subdomain->status != GPD_STATE_POWER_OFF
113317b75ecaSRafael J. Wysocki 	    && new_subdomain->status != GPD_STATE_ACTIVE) {
113417b75ecaSRafael J. Wysocki 		mutex_unlock(&new_subdomain->lock);
113517b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
113617b75ecaSRafael J. Wysocki 		goto start;
113717b75ecaSRafael J. Wysocki 	}
113817b75ecaSRafael J. Wysocki 
113917b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
114017b75ecaSRafael J. Wysocki 	    &&  new_subdomain->status != GPD_STATE_POWER_OFF) {
1141f721889fSRafael J. Wysocki 		ret = -EINVAL;
1142f721889fSRafael J. Wysocki 		goto out;
1143f721889fSRafael J. Wysocki 	}
1144f721889fSRafael J. Wysocki 
1145f721889fSRafael J. Wysocki 	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
1146f721889fSRafael J. Wysocki 		if (subdomain == new_subdomain) {
1147f721889fSRafael J. Wysocki 			ret = -EINVAL;
1148f721889fSRafael J. Wysocki 			goto out;
1149f721889fSRafael J. Wysocki 		}
1150f721889fSRafael J. Wysocki 	}
1151f721889fSRafael J. Wysocki 
1152f721889fSRafael J. Wysocki 	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
1153f721889fSRafael J. Wysocki 	new_subdomain->parent = genpd;
115417b75ecaSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1155f721889fSRafael J. Wysocki 		genpd->sd_count++;
1156f721889fSRafael J. Wysocki 
1157f721889fSRafael J. Wysocki  out:
115817b75ecaSRafael J. Wysocki 	mutex_unlock(&new_subdomain->lock);
115917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1160f721889fSRafael J. Wysocki 
1161f721889fSRafael J. Wysocki 	return ret;
1162f721889fSRafael J. Wysocki }
1163f721889fSRafael J. Wysocki 
1164f721889fSRafael J. Wysocki /**
1165f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1166f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
1167f721889fSRafael J. Wysocki  * @target: Subdomain to be removed.
1168f721889fSRafael J. Wysocki  */
1169f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
1170f721889fSRafael J. Wysocki 			      struct generic_pm_domain *target)
1171f721889fSRafael J. Wysocki {
1172f721889fSRafael J. Wysocki 	struct generic_pm_domain *subdomain;
1173f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1174f721889fSRafael J. Wysocki 
1175f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
1176f721889fSRafael J. Wysocki 		return -EINVAL;
1177f721889fSRafael J. Wysocki 
117817b75ecaSRafael J. Wysocki  start:
117917b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1180f721889fSRafael J. Wysocki 
1181f721889fSRafael J. Wysocki 	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
1182f721889fSRafael J. Wysocki 		if (subdomain != target)
1183f721889fSRafael J. Wysocki 			continue;
1184f721889fSRafael J. Wysocki 
1185f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1186f721889fSRafael J. Wysocki 
118717b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
118817b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
118917b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
119017b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
119117b75ecaSRafael J. Wysocki 			goto start;
119217b75ecaSRafael J. Wysocki 		}
119317b75ecaSRafael J. Wysocki 
1194f721889fSRafael J. Wysocki 		list_del(&subdomain->sd_node);
1195f721889fSRafael J. Wysocki 		subdomain->parent = NULL;
119617b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1197f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1198f721889fSRafael J. Wysocki 
1199f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1200f721889fSRafael J. Wysocki 
1201f721889fSRafael J. Wysocki 		ret = 0;
1202f721889fSRafael J. Wysocki 		break;
1203f721889fSRafael J. Wysocki 	}
1204f721889fSRafael J. Wysocki 
120517b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1206f721889fSRafael J. Wysocki 
1207f721889fSRafael J. Wysocki 	return ret;
1208f721889fSRafael J. Wysocki }
1209f721889fSRafael J. Wysocki 
1210f721889fSRafael J. Wysocki /**
1211f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1212f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1213f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1214f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1215f721889fSRafael J. Wysocki  */
1216f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1217f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1218f721889fSRafael J. Wysocki {
1219f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1220f721889fSRafael J. Wysocki 		return;
1221f721889fSRafael J. Wysocki 
1222f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->sd_node);
1223f721889fSRafael J. Wysocki 	genpd->parent = NULL;
1224f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1225f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->sd_list);
1226f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1227f721889fSRafael J. Wysocki 	genpd->gov = gov;
1228f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1229f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1230f721889fSRafael J. Wysocki 	genpd->sd_count = 0;
123117b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
123217b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1233c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1234c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1235596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1236596ba34bSRafael J. Wysocki 	genpd->suspended_count = 0;
1237f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1238f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1239f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1240596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1241596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
1242596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1243596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
1244596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1245596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
1246596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1247596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
1248596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1249596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
1250596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
1251596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
1252596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_restore;
1253596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
12545125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
12555125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
12565125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
12575125bbf3SRafael J. Wysocki }
12585125bbf3SRafael J. Wysocki 
12595125bbf3SRafael J. Wysocki /**
12605125bbf3SRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
12615125bbf3SRafael J. Wysocki  */
12625125bbf3SRafael J. Wysocki void pm_genpd_poweroff_unused(void)
12635125bbf3SRafael J. Wysocki {
12645125bbf3SRafael J. Wysocki 	struct generic_pm_domain *genpd;
12655125bbf3SRafael J. Wysocki 
12665125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
12675125bbf3SRafael J. Wysocki 
12685125bbf3SRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
12695125bbf3SRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
12705125bbf3SRafael J. Wysocki 
12715125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
1272f721889fSRafael J. Wysocki }
1273