xref: /openbmc/linux/drivers/base/power/domain.c (revision 697a7f37)
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 
195248051bSRafael J. Wysocki #ifdef CONFIG_PM
205248051bSRafael J. Wysocki 
215248051bSRafael J. Wysocki static struct generic_pm_domain *dev_to_genpd(struct device *dev)
225248051bSRafael J. Wysocki {
235248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
245248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
255248051bSRafael J. Wysocki 
26596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
275248051bSRafael J. Wysocki }
28f721889fSRafael J. Wysocki 
29f721889fSRafael J. Wysocki static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
30f721889fSRafael J. Wysocki {
31f721889fSRafael J. Wysocki 	if (!WARN_ON(genpd->sd_count == 0))
32f721889fSRafael J. Wysocki 			genpd->sd_count--;
33f721889fSRafael J. Wysocki }
34f721889fSRafael J. Wysocki 
3517b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
3617b75ecaSRafael J. Wysocki {
3717b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
3817b75ecaSRafael J. Wysocki 
3917b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
4017b75ecaSRafael J. Wysocki 	/*
4117b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
4217b75ecaSRafael J. Wysocki 	 * or the power off state.
4317b75ecaSRafael J. Wysocki 	 */
4417b75ecaSRafael J. Wysocki 	for (;;) {
4517b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
4617b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
47c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
48c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
4917b75ecaSRafael J. Wysocki 			break;
5017b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
5117b75ecaSRafael J. Wysocki 
5217b75ecaSRafael J. Wysocki 		schedule();
5317b75ecaSRafael J. Wysocki 
5417b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
5517b75ecaSRafael J. Wysocki 	}
5617b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
5717b75ecaSRafael J. Wysocki }
5817b75ecaSRafael J. Wysocki 
5917b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
6017b75ecaSRafael J. Wysocki {
6117b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
6217b75ecaSRafael J. Wysocki }
6317b75ecaSRafael J. Wysocki 
64c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
65c6d22b37SRafael J. Wysocki {
66c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
67c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
68c6d22b37SRafael J. Wysocki }
69c6d22b37SRafael J. Wysocki 
70f721889fSRafael J. Wysocki /**
715248051bSRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its parents.
725248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
735248051bSRafael J. Wysocki  *
745248051bSRafael J. Wysocki  * Restore power to @genpd and all of its parents so that it is possible to
755248051bSRafael J. Wysocki  * resume a device belonging to it.
765248051bSRafael J. Wysocki  */
7718b4f3f5SMagnus Damm int pm_genpd_poweron(struct generic_pm_domain *genpd)
785248051bSRafael J. Wysocki {
7917b75ecaSRafael J. Wysocki 	struct generic_pm_domain *parent = genpd->parent;
8017b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
815248051bSRafael J. Wysocki 	int ret = 0;
825248051bSRafael J. Wysocki 
835248051bSRafael J. Wysocki  start:
8417b75ecaSRafael J. Wysocki 	if (parent) {
85c6d22b37SRafael J. Wysocki 		genpd_acquire_lock(parent);
865248051bSRafael J. Wysocki 		mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
8717b75ecaSRafael J. Wysocki 	} else {
8817b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
8917b75ecaSRafael J. Wysocki 	}
9017b75ecaSRafael J. Wysocki 
9117b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
92596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
935248051bSRafael J. Wysocki 		goto out;
945248051bSRafael J. Wysocki 
95c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
96c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
97c6d22b37SRafael J. Wysocki 		goto out;
98c6d22b37SRafael J. Wysocki 	}
99c6d22b37SRafael J. Wysocki 
10017b75ecaSRafael J. Wysocki 	if (parent && parent->status != GPD_STATE_ACTIVE) {
1015248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
102c6d22b37SRafael J. Wysocki 		genpd_release_lock(parent);
1035248051bSRafael J. Wysocki 
10417b75ecaSRafael J. Wysocki 		ret = pm_genpd_poweron(parent);
1055248051bSRafael J. Wysocki 		if (ret)
1065248051bSRafael J. Wysocki 			return ret;
1075248051bSRafael J. Wysocki 
1085248051bSRafael J. Wysocki 		goto start;
1095248051bSRafael J. Wysocki 	}
1105248051bSRafael J. Wysocki 
1115248051bSRafael J. Wysocki 	if (genpd->power_on) {
1125248051bSRafael J. Wysocki 		int ret = genpd->power_on(genpd);
1135248051bSRafael J. Wysocki 		if (ret)
1145248051bSRafael J. Wysocki 			goto out;
1155248051bSRafael J. Wysocki 	}
1165248051bSRafael J. Wysocki 
117c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
11817b75ecaSRafael J. Wysocki 	if (parent)
11917b75ecaSRafael J. Wysocki 		parent->sd_count++;
1205248051bSRafael J. Wysocki 
1215248051bSRafael J. Wysocki  out:
1225248051bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
12317b75ecaSRafael J. Wysocki 	if (parent)
124c6d22b37SRafael J. Wysocki 		genpd_release_lock(parent);
1255248051bSRafael J. Wysocki 
1265248051bSRafael J. Wysocki 	return ret;
1275248051bSRafael J. Wysocki }
1285248051bSRafael J. Wysocki 
1295248051bSRafael J. Wysocki #endif /* CONFIG_PM */
1305248051bSRafael J. Wysocki 
1315248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
1325248051bSRafael J. Wysocki 
1335248051bSRafael J. Wysocki /**
134f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
135f721889fSRafael J. Wysocki  * @dle: Device list entry of the device to save the state of.
136f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
137f721889fSRafael J. Wysocki  */
138f721889fSRafael J. Wysocki static int __pm_genpd_save_device(struct dev_list_entry *dle,
139f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
14017b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
141f721889fSRafael J. Wysocki {
142f721889fSRafael J. Wysocki 	struct device *dev = dle->dev;
143f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
144f721889fSRafael J. Wysocki 	int ret = 0;
145f721889fSRafael J. Wysocki 
146f721889fSRafael J. Wysocki 	if (dle->need_restore)
147f721889fSRafael J. Wysocki 		return 0;
148f721889fSRafael J. Wysocki 
14917b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
15017b75ecaSRafael J. Wysocki 
151f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_suspend) {
152f721889fSRafael J. Wysocki 		if (genpd->start_device)
153f721889fSRafael J. Wysocki 			genpd->start_device(dev);
154f721889fSRafael J. Wysocki 
155f721889fSRafael J. Wysocki 		ret = drv->pm->runtime_suspend(dev);
156f721889fSRafael J. Wysocki 
157f721889fSRafael J. Wysocki 		if (genpd->stop_device)
158f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
159f721889fSRafael J. Wysocki 	}
160f721889fSRafael J. Wysocki 
16117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
16217b75ecaSRafael J. Wysocki 
163f721889fSRafael J. Wysocki 	if (!ret)
164f721889fSRafael J. Wysocki 		dle->need_restore = true;
165f721889fSRafael J. Wysocki 
166f721889fSRafael J. Wysocki 	return ret;
167f721889fSRafael J. Wysocki }
168f721889fSRafael J. Wysocki 
169f721889fSRafael J. Wysocki /**
170f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
171f721889fSRafael J. Wysocki  * @dle: Device list entry of the device to restore the state of.
172f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
173f721889fSRafael J. Wysocki  */
174f721889fSRafael J. Wysocki static void __pm_genpd_restore_device(struct dev_list_entry *dle,
175f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
17617b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
177f721889fSRafael J. Wysocki {
178f721889fSRafael J. Wysocki 	struct device *dev = dle->dev;
179f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
180f721889fSRafael J. Wysocki 
181f721889fSRafael J. Wysocki 	if (!dle->need_restore)
182f721889fSRafael J. Wysocki 		return;
183f721889fSRafael J. Wysocki 
18417b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
18517b75ecaSRafael J. Wysocki 
186f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_resume) {
187f721889fSRafael J. Wysocki 		if (genpd->start_device)
188f721889fSRafael J. Wysocki 			genpd->start_device(dev);
189f721889fSRafael J. Wysocki 
190f721889fSRafael J. Wysocki 		drv->pm->runtime_resume(dev);
191f721889fSRafael J. Wysocki 
192f721889fSRafael J. Wysocki 		if (genpd->stop_device)
193f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
194f721889fSRafael J. Wysocki 	}
195f721889fSRafael J. Wysocki 
19617b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
19717b75ecaSRafael J. Wysocki 
198f721889fSRafael J. Wysocki 	dle->need_restore = false;
199f721889fSRafael J. Wysocki }
200f721889fSRafael J. Wysocki 
201f721889fSRafael J. Wysocki /**
202c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
203c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
204c6d22b37SRafael J. Wysocki  *
205c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
206c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
207c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
208c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
209c6d22b37SRafael J. Wysocki  */
210c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
211c6d22b37SRafael J. Wysocki {
212c6d22b37SRafael J. Wysocki 	return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
213c6d22b37SRafael J. Wysocki }
214c6d22b37SRafael J. Wysocki 
215c6d22b37SRafael J. Wysocki /**
216f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
217f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
218f721889fSRafael J. Wysocki  *
219f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
220f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
221f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
222f721889fSRafael J. Wysocki  */
223f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
22417b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
225f721889fSRafael J. Wysocki {
226f721889fSRafael J. Wysocki 	struct generic_pm_domain *parent;
227f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
228f721889fSRafael J. Wysocki 	unsigned int not_suspended;
229c6d22b37SRafael J. Wysocki 	int ret = 0;
230f721889fSRafael J. Wysocki 
231c6d22b37SRafael J. Wysocki  start:
232c6d22b37SRafael J. Wysocki 	/*
233c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
234c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
235c6d22b37SRafael J. Wysocki 	 * (2) System suspend is in progress.
236c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
237c6d22b37SRafael J. Wysocki 	 */
238c6d22b37SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0
239c6d22b37SRafael J. Wysocki 	    || genpd->resume_count > 0)
240f721889fSRafael J. Wysocki 		return 0;
241f721889fSRafael J. Wysocki 
242f721889fSRafael J. Wysocki 	if (genpd->sd_count > 0)
243f721889fSRafael J. Wysocki 		return -EBUSY;
244f721889fSRafael J. Wysocki 
245f721889fSRafael J. Wysocki 	not_suspended = 0;
246f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node)
247f721889fSRafael J. Wysocki 		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
248f721889fSRafael J. Wysocki 			not_suspended++;
249f721889fSRafael J. Wysocki 
250f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
251f721889fSRafael J. Wysocki 		return -EBUSY;
252f721889fSRafael J. Wysocki 
253c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
254c6d22b37SRafael J. Wysocki 		/*
255c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
256c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
257c6d22b37SRafael J. Wysocki 		 */
258c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
259c6d22b37SRafael J. Wysocki 		return 0;
260c6d22b37SRafael J. Wysocki 	}
261c6d22b37SRafael J. Wysocki 
262f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
263f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
264f721889fSRafael J. Wysocki 			return -EAGAIN;
265f721889fSRafael J. Wysocki 	}
266f721889fSRafael J. Wysocki 
26717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
268c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
26917b75ecaSRafael J. Wysocki 
270f721889fSRafael J. Wysocki 	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
271f721889fSRafael J. Wysocki 		ret = __pm_genpd_save_device(dle, genpd);
272697a7f37SRafael J. Wysocki 		if (ret) {
273697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
274697a7f37SRafael J. Wysocki 			goto out;
275697a7f37SRafael J. Wysocki 		}
276f721889fSRafael J. Wysocki 
277c6d22b37SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
278c6d22b37SRafael J. Wysocki 			goto out;
279c6d22b37SRafael J. Wysocki 
280c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
281c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
282c6d22b37SRafael J. Wysocki 			goto start;
283c6d22b37SRafael J. Wysocki 		}
284c6d22b37SRafael J. Wysocki 	}
28517b75ecaSRafael J. Wysocki 
28617b75ecaSRafael J. Wysocki 	parent = genpd->parent;
28717b75ecaSRafael J. Wysocki 	if (parent) {
288c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
289c6d22b37SRafael J. Wysocki 
29017b75ecaSRafael J. Wysocki 		genpd_acquire_lock(parent);
29117b75ecaSRafael J. Wysocki 		mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
292c6d22b37SRafael J. Wysocki 
293c6d22b37SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd)) {
294c6d22b37SRafael J. Wysocki 			genpd_release_lock(parent);
295c6d22b37SRafael J. Wysocki 			goto out;
296c6d22b37SRafael J. Wysocki 		}
29717b75ecaSRafael J. Wysocki 	}
29817b75ecaSRafael J. Wysocki 
299f721889fSRafael J. Wysocki 	if (genpd->power_off)
300f721889fSRafael J. Wysocki 		genpd->power_off(genpd);
301f721889fSRafael J. Wysocki 
30217b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
303f721889fSRafael J. Wysocki 
304f721889fSRafael J. Wysocki 	if (parent) {
305f721889fSRafael J. Wysocki 		genpd_sd_counter_dec(parent);
306f721889fSRafael J. Wysocki 		if (parent->sd_count == 0)
307f721889fSRafael J. Wysocki 			queue_work(pm_wq, &parent->power_off_work);
30817b75ecaSRafael J. Wysocki 
30917b75ecaSRafael J. Wysocki 		genpd_release_lock(parent);
310f721889fSRafael J. Wysocki 	}
311f721889fSRafael J. Wysocki 
312c6d22b37SRafael J. Wysocki  out:
313c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
314c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
315c6d22b37SRafael J. Wysocki 	return ret;
316f721889fSRafael J. Wysocki }
317f721889fSRafael J. Wysocki 
318f721889fSRafael J. Wysocki /**
319f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
320f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
321f721889fSRafael J. Wysocki  */
322f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
323f721889fSRafael J. Wysocki {
324f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
325f721889fSRafael J. Wysocki 
326f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
327f721889fSRafael J. Wysocki 
32817b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
329f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
33017b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
331f721889fSRafael J. Wysocki }
332f721889fSRafael J. Wysocki 
333f721889fSRafael J. Wysocki /**
334f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
335f721889fSRafael J. Wysocki  * @dev: Device to suspend.
336f721889fSRafael J. Wysocki  *
337f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
338f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
339f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
340f721889fSRafael J. Wysocki  */
341f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
342f721889fSRafael J. Wysocki {
343f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
344f721889fSRafael J. Wysocki 
345f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
346f721889fSRafael J. Wysocki 
3475248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
3485248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
349f721889fSRafael J. Wysocki 		return -EINVAL;
350f721889fSRafael J. Wysocki 
351f721889fSRafael J. Wysocki 	if (genpd->stop_device) {
352f721889fSRafael J. Wysocki 		int ret = genpd->stop_device(dev);
353f721889fSRafael J. Wysocki 		if (ret)
35417b75ecaSRafael J. Wysocki 			return ret;
355f721889fSRafael J. Wysocki 	}
35617b75ecaSRafael J. Wysocki 
357c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
358f721889fSRafael J. Wysocki 	genpd->in_progress++;
359f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
360f721889fSRafael J. Wysocki 	genpd->in_progress--;
361c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
362f721889fSRafael J. Wysocki 
363f721889fSRafael J. Wysocki 	return 0;
364f721889fSRafael J. Wysocki }
365f721889fSRafael J. Wysocki 
366f721889fSRafael J. Wysocki /**
367596ba34bSRafael J. Wysocki  * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
368596ba34bSRafael J. Wysocki  * @dev: Device to resume.
369596ba34bSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
370596ba34bSRafael J. Wysocki  */
371596ba34bSRafael J. Wysocki static void __pm_genpd_runtime_resume(struct device *dev,
372596ba34bSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
373596ba34bSRafael J. Wysocki {
374596ba34bSRafael J. Wysocki 	struct dev_list_entry *dle;
375596ba34bSRafael J. Wysocki 
376596ba34bSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node) {
377596ba34bSRafael J. Wysocki 		if (dle->dev == dev) {
378596ba34bSRafael J. Wysocki 			__pm_genpd_restore_device(dle, genpd);
379596ba34bSRafael J. Wysocki 			break;
380596ba34bSRafael J. Wysocki 		}
381596ba34bSRafael J. Wysocki 	}
382596ba34bSRafael J. Wysocki }
383596ba34bSRafael J. Wysocki 
384596ba34bSRafael J. Wysocki /**
385f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
386f721889fSRafael J. Wysocki  * @dev: Device to resume.
387f721889fSRafael J. Wysocki  *
388f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
389f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
390f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
391f721889fSRafael J. Wysocki  */
392f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
393f721889fSRafael J. Wysocki {
394f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
395c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
396f721889fSRafael J. Wysocki 	int ret;
397f721889fSRafael J. Wysocki 
398f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
399f721889fSRafael J. Wysocki 
4005248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
4015248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
402f721889fSRafael J. Wysocki 		return -EINVAL;
403f721889fSRafael J. Wysocki 
404f721889fSRafael J. Wysocki 	ret = pm_genpd_poweron(genpd);
405f721889fSRafael J. Wysocki 	if (ret)
406f721889fSRafael J. Wysocki 		return ret;
407f721889fSRafael J. Wysocki 
408c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
40917b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
410c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
411c6d22b37SRafael J. Wysocki 	for (;;) {
412c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
413c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
414c6d22b37SRafael J. Wysocki 		/*
415c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
416c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
417c6d22b37SRafael J. Wysocki 		 * not wait.
418c6d22b37SRafael J. Wysocki 		 */
419c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
420c6d22b37SRafael J. Wysocki 			break;
421c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
422c6d22b37SRafael J. Wysocki 
423c6d22b37SRafael J. Wysocki 		schedule();
424c6d22b37SRafael J. Wysocki 
425c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
426c6d22b37SRafael J. Wysocki 	}
427c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
428596ba34bSRafael J. Wysocki 	__pm_genpd_runtime_resume(dev, genpd);
429c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
430c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
43117b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
432c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
43317b75ecaSRafael J. Wysocki 
43417b75ecaSRafael J. Wysocki 	if (genpd->start_device)
43517b75ecaSRafael J. Wysocki 		genpd->start_device(dev);
436f721889fSRafael J. Wysocki 
437f721889fSRafael J. Wysocki 	return 0;
438f721889fSRafael J. Wysocki }
439f721889fSRafael J. Wysocki 
440f721889fSRafael J. Wysocki #else
441f721889fSRafael J. Wysocki 
442f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
443596ba34bSRafael J. Wysocki static inline void __pm_genpd_runtime_resume(struct device *dev,
444596ba34bSRafael J. Wysocki 					     struct generic_pm_domain *genpd) {}
445f721889fSRafael J. Wysocki 
446f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
447f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
448f721889fSRafael J. Wysocki 
449f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
450f721889fSRafael J. Wysocki 
451596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
452596ba34bSRafael J. Wysocki 
453596ba34bSRafael J. Wysocki /**
454596ba34bSRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
455596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
456596ba34bSRafael J. Wysocki  *
457596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
458596ba34bSRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its parent.
459596ba34bSRafael J. Wysocki  *
460596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
461596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
462596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
463596ba34bSRafael J. Wysocki  */
464596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
465596ba34bSRafael J. Wysocki {
466596ba34bSRafael J. Wysocki 	struct generic_pm_domain *parent = genpd->parent;
467596ba34bSRafael J. Wysocki 
46817b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
469596ba34bSRafael J. Wysocki 		return;
470596ba34bSRafael J. Wysocki 
471596ba34bSRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
472596ba34bSRafael J. Wysocki 		return;
473596ba34bSRafael J. Wysocki 
474596ba34bSRafael J. Wysocki 	if (genpd->power_off)
475596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
476596ba34bSRafael J. Wysocki 
47717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
478596ba34bSRafael J. Wysocki 	if (parent) {
479596ba34bSRafael J. Wysocki 		genpd_sd_counter_dec(parent);
480596ba34bSRafael J. Wysocki 		pm_genpd_sync_poweroff(parent);
481596ba34bSRafael J. Wysocki 	}
482596ba34bSRafael J. Wysocki }
483596ba34bSRafael J. Wysocki 
484596ba34bSRafael J. Wysocki /**
485596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
486596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
487596ba34bSRafael J. Wysocki  *
488596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
489596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
490596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
491596ba34bSRafael J. Wysocki  * consisting of I/O devices.
492596ba34bSRafael J. Wysocki  */
493596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
494596ba34bSRafael J. Wysocki {
495596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
496b6c10c84SRafael J. Wysocki 	int ret;
497596ba34bSRafael J. Wysocki 
498596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
499596ba34bSRafael J. Wysocki 
500596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
501596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
502596ba34bSRafael J. Wysocki 		return -EINVAL;
503596ba34bSRafael J. Wysocki 
50417b75ecaSRafael J. Wysocki 	/*
50517b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
50617b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
50717b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
50817b75ecaSRafael J. Wysocki 	 */
50917b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
51017b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
51117b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
51217b75ecaSRafael J. Wysocki 
51317b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
51417b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
51517b75ecaSRafael J. Wysocki 		return -EBUSY;
51617b75ecaSRafael J. Wysocki 	}
51717b75ecaSRafael J. Wysocki 
51817b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
519596ba34bSRafael J. Wysocki 
520596ba34bSRafael J. Wysocki 	if (genpd->prepared_count++ == 0)
52117b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
52217b75ecaSRafael J. Wysocki 
52317b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
524596ba34bSRafael J. Wysocki 
525596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
52617b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
527596ba34bSRafael J. Wysocki 		return 0;
528596ba34bSRafael J. Wysocki 	}
529596ba34bSRafael J. Wysocki 
530596ba34bSRafael J. Wysocki 	/*
53117b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
53217b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
53317b75ecaSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by .stop_device()), we need
53417b75ecaSRafael J. Wysocki 	 * to make it operational.
535596ba34bSRafael J. Wysocki 	 */
53617b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
537596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
538596ba34bSRafael J. Wysocki 
539b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
540b6c10c84SRafael J. Wysocki 	if (ret) {
541b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
542b6c10c84SRafael J. Wysocki 
543b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
544b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
545b6c10c84SRafael J. Wysocki 
546b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
54717b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
548b6c10c84SRafael J. Wysocki 	}
54917b75ecaSRafael J. Wysocki 
55017b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
551b6c10c84SRafael J. Wysocki 	return ret;
552596ba34bSRafael J. Wysocki }
553596ba34bSRafael J. Wysocki 
554596ba34bSRafael J. Wysocki /**
555596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
556596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
557596ba34bSRafael J. Wysocki  *
558596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
559596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
560596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
561596ba34bSRafael J. Wysocki  */
562596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
563596ba34bSRafael J. Wysocki {
564596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
565596ba34bSRafael J. Wysocki 
566596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
567596ba34bSRafael J. Wysocki 
568596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
569596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
570596ba34bSRafael J. Wysocki 		return -EINVAL;
571596ba34bSRafael J. Wysocki 
572596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
573596ba34bSRafael J. Wysocki }
574596ba34bSRafael J. Wysocki 
575596ba34bSRafael J. Wysocki /**
576596ba34bSRafael J. Wysocki  * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
577596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
578596ba34bSRafael J. Wysocki  *
579596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
580596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
581596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
582596ba34bSRafael J. Wysocki  */
583596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
584596ba34bSRafael J. Wysocki {
585596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
586596ba34bSRafael J. Wysocki 	int ret;
587596ba34bSRafael J. Wysocki 
588596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
589596ba34bSRafael J. Wysocki 
590596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
591596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
592596ba34bSRafael J. Wysocki 		return -EINVAL;
593596ba34bSRafael J. Wysocki 
594596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
595596ba34bSRafael J. Wysocki 		return 0;
596596ba34bSRafael J. Wysocki 
597596ba34bSRafael J. Wysocki 	ret = pm_generic_suspend_noirq(dev);
598596ba34bSRafael J. Wysocki 	if (ret)
599596ba34bSRafael J. Wysocki 		return ret;
600596ba34bSRafael J. Wysocki 
601d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
602d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
603d4f2d87aSRafael J. Wysocki 		return 0;
604d4f2d87aSRafael J. Wysocki 
605596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
606596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
607596ba34bSRafael J. Wysocki 
608596ba34bSRafael J. Wysocki 	/*
609596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
610596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
611596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
612596ba34bSRafael J. Wysocki 	 */
613596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
614596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
615596ba34bSRafael J. Wysocki 
616596ba34bSRafael J. Wysocki 	return 0;
617596ba34bSRafael J. Wysocki }
618596ba34bSRafael J. Wysocki 
619596ba34bSRafael J. Wysocki /**
620596ba34bSRafael J. Wysocki  * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
621596ba34bSRafael J. Wysocki  * @dev: Device to resume.
622596ba34bSRafael J. Wysocki  *
623596ba34bSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
624596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
625596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
626596ba34bSRafael J. Wysocki  * devices.
627596ba34bSRafael J. Wysocki  */
628596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
629596ba34bSRafael J. Wysocki {
630596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
631596ba34bSRafael J. Wysocki 
632596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
633596ba34bSRafael J. Wysocki 
634596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
635596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
636596ba34bSRafael J. Wysocki 		return -EINVAL;
637596ba34bSRafael J. Wysocki 
638596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
639596ba34bSRafael J. Wysocki 		return 0;
640596ba34bSRafael J. Wysocki 
641596ba34bSRafael J. Wysocki 	/*
642596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
643596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
644596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
645596ba34bSRafael J. Wysocki 	 */
646596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
647596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
648596ba34bSRafael J. Wysocki 	if (genpd->start_device)
649596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
650596ba34bSRafael J. Wysocki 
651596ba34bSRafael J. Wysocki 	return pm_generic_resume_noirq(dev);
652596ba34bSRafael J. Wysocki }
653596ba34bSRafael J. Wysocki 
654596ba34bSRafael J. Wysocki /**
655596ba34bSRafael J. Wysocki  * pm_genpd_resume - Resume a device belonging to an I/O power domain.
656596ba34bSRafael J. Wysocki  * @dev: Device to resume.
657596ba34bSRafael J. Wysocki  *
658596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
659596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
660596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
661596ba34bSRafael J. Wysocki  */
662596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
663596ba34bSRafael J. Wysocki {
664596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
665596ba34bSRafael J. Wysocki 
666596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
667596ba34bSRafael J. Wysocki 
668596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
669596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
670596ba34bSRafael J. Wysocki 		return -EINVAL;
671596ba34bSRafael J. Wysocki 
672596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
673596ba34bSRafael J. Wysocki }
674596ba34bSRafael J. Wysocki 
675596ba34bSRafael J. Wysocki /**
676596ba34bSRafael J. Wysocki  * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
677596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
678596ba34bSRafael J. Wysocki  *
679596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
680596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
681596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
682596ba34bSRafael J. Wysocki  */
683596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
684596ba34bSRafael J. Wysocki {
685596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
686596ba34bSRafael J. Wysocki 
687596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
688596ba34bSRafael J. Wysocki 
689596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
690596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
691596ba34bSRafael J. Wysocki 		return -EINVAL;
692596ba34bSRafael J. Wysocki 
693596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
694596ba34bSRafael J. Wysocki }
695596ba34bSRafael J. Wysocki 
696596ba34bSRafael J. Wysocki /**
697596ba34bSRafael J. Wysocki  * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
698596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
699596ba34bSRafael J. Wysocki  *
700596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
701596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
702596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
703596ba34bSRafael J. Wysocki  * devices.
704596ba34bSRafael J. Wysocki  */
705596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
706596ba34bSRafael J. Wysocki {
707596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
708596ba34bSRafael J. Wysocki 	int ret;
709596ba34bSRafael J. Wysocki 
710596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
711596ba34bSRafael J. Wysocki 
712596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
713596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
714596ba34bSRafael J. Wysocki 		return -EINVAL;
715596ba34bSRafael J. Wysocki 
716596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
717596ba34bSRafael J. Wysocki 		return 0;
718596ba34bSRafael J. Wysocki 
719596ba34bSRafael J. Wysocki 	ret = pm_generic_freeze_noirq(dev);
720596ba34bSRafael J. Wysocki 	if (ret)
721596ba34bSRafael J. Wysocki 		return ret;
722596ba34bSRafael J. Wysocki 
723596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
724596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
725596ba34bSRafael J. Wysocki 
726596ba34bSRafael J. Wysocki 	return 0;
727596ba34bSRafael J. Wysocki }
728596ba34bSRafael J. Wysocki 
729596ba34bSRafael J. Wysocki /**
730596ba34bSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
731596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
732596ba34bSRafael J. Wysocki  *
733596ba34bSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
734596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
735596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
736596ba34bSRafael J. Wysocki  * devices.
737596ba34bSRafael J. Wysocki  */
738596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
739596ba34bSRafael J. Wysocki {
740596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
741596ba34bSRafael J. Wysocki 
742596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
743596ba34bSRafael J. Wysocki 
744596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
745596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
746596ba34bSRafael J. Wysocki 		return -EINVAL;
747596ba34bSRafael J. Wysocki 
748596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
749596ba34bSRafael J. Wysocki 		return 0;
750596ba34bSRafael J. Wysocki 
751596ba34bSRafael J. Wysocki 	if (genpd->start_device)
752596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
753596ba34bSRafael J. Wysocki 
754596ba34bSRafael J. Wysocki 	return pm_generic_thaw_noirq(dev);
755596ba34bSRafael J. Wysocki }
756596ba34bSRafael J. Wysocki 
757596ba34bSRafael J. Wysocki /**
758596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
759596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
760596ba34bSRafael J. Wysocki  *
761596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
762596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
763596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
764596ba34bSRafael J. Wysocki  */
765596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
766596ba34bSRafael J. Wysocki {
767596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
768596ba34bSRafael J. Wysocki 
769596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
770596ba34bSRafael J. Wysocki 
771596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
772596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
773596ba34bSRafael J. Wysocki 		return -EINVAL;
774596ba34bSRafael J. Wysocki 
775596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
776596ba34bSRafael J. Wysocki }
777596ba34bSRafael J. Wysocki 
778596ba34bSRafael J. Wysocki /**
779596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
780596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
781596ba34bSRafael J. Wysocki  *
782596ba34bSRafael J. Wysocki  * Power off a device under the assumption that its pm_domain field points to
783596ba34bSRafael J. Wysocki  * the domain member of an object of type struct generic_pm_domain representing
784596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
785596ba34bSRafael J. Wysocki  */
786596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev)
787596ba34bSRafael J. Wysocki {
788596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
789596ba34bSRafael J. Wysocki 
790596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
791596ba34bSRafael J. Wysocki 
792596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
793596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
794596ba34bSRafael J. Wysocki 		return -EINVAL;
795596ba34bSRafael J. Wysocki 
796596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
797596ba34bSRafael J. Wysocki }
798596ba34bSRafael J. Wysocki 
799596ba34bSRafael J. Wysocki /**
800596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
801596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
802596ba34bSRafael J. Wysocki  *
803596ba34bSRafael J. Wysocki  * Carry out a late powering off of a device under the assumption that its
804596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
805596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
806596ba34bSRafael J. Wysocki  */
807596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev)
808596ba34bSRafael J. Wysocki {
809596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
810596ba34bSRafael J. Wysocki 	int ret;
811596ba34bSRafael J. Wysocki 
812596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
813596ba34bSRafael J. Wysocki 
814596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
815596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
816596ba34bSRafael J. Wysocki 		return -EINVAL;
817596ba34bSRafael J. Wysocki 
818596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
819596ba34bSRafael J. Wysocki 		return 0;
820596ba34bSRafael J. Wysocki 
821596ba34bSRafael J. Wysocki 	ret = pm_generic_poweroff_noirq(dev);
822596ba34bSRafael J. Wysocki 	if (ret)
823596ba34bSRafael J. Wysocki 		return ret;
824596ba34bSRafael J. Wysocki 
825d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
826d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
827d4f2d87aSRafael J. Wysocki 		return 0;
828d4f2d87aSRafael J. Wysocki 
829596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
830596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
831596ba34bSRafael J. Wysocki 
832596ba34bSRafael J. Wysocki 	/*
833596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
834596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
835596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
836596ba34bSRafael J. Wysocki 	 */
837596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
838596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
839596ba34bSRafael J. Wysocki 
840596ba34bSRafael J. Wysocki 	return 0;
841596ba34bSRafael J. Wysocki }
842596ba34bSRafael J. Wysocki 
843596ba34bSRafael J. Wysocki /**
844596ba34bSRafael J. Wysocki  * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
845596ba34bSRafael J. Wysocki  * @dev: Device to resume.
846596ba34bSRafael J. Wysocki  *
847596ba34bSRafael J. Wysocki  * Carry out an early restore of a device under the assumption that its
848596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
849596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
850596ba34bSRafael J. Wysocki  * devices.
851596ba34bSRafael J. Wysocki  */
852596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
853596ba34bSRafael J. Wysocki {
854596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
855596ba34bSRafael J. Wysocki 
856596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
857596ba34bSRafael J. Wysocki 
858596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
859596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
860596ba34bSRafael J. Wysocki 		return -EINVAL;
861596ba34bSRafael J. Wysocki 
862596ba34bSRafael J. Wysocki 	/*
863596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
864596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
865596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
866596ba34bSRafael J. Wysocki 	 */
86717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
868596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
869596ba34bSRafael J. Wysocki 		/*
870596ba34bSRafael J. Wysocki 		 * The boot kernel might put the domain into the power on state,
871596ba34bSRafael J. Wysocki 		 * so make sure it really is powered off.
872596ba34bSRafael J. Wysocki 		 */
873596ba34bSRafael J. Wysocki 		if (genpd->power_off)
874596ba34bSRafael J. Wysocki 			genpd->power_off(genpd);
875596ba34bSRafael J. Wysocki 		return 0;
876596ba34bSRafael J. Wysocki 	}
877596ba34bSRafael J. Wysocki 
878596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
879596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
880596ba34bSRafael J. Wysocki 	if (genpd->start_device)
881596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
882596ba34bSRafael J. Wysocki 
883596ba34bSRafael J. Wysocki 	return pm_generic_restore_noirq(dev);
884596ba34bSRafael J. Wysocki }
885596ba34bSRafael J. Wysocki 
886596ba34bSRafael J. Wysocki /**
887596ba34bSRafael J. Wysocki  * pm_genpd_restore - Restore a device belonging to an I/O power domain.
888596ba34bSRafael J. Wysocki  * @dev: Device to resume.
889596ba34bSRafael J. Wysocki  *
890596ba34bSRafael J. Wysocki  * Restore a device under the assumption that its pm_domain field points to the
891596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
892596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
893596ba34bSRafael J. Wysocki  */
894596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev)
895596ba34bSRafael J. Wysocki {
896596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
897596ba34bSRafael J. Wysocki 
898596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
899596ba34bSRafael J. Wysocki 
900596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
901596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
902596ba34bSRafael J. Wysocki 		return -EINVAL;
903596ba34bSRafael J. Wysocki 
904596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
905596ba34bSRafael J. Wysocki }
906596ba34bSRafael J. Wysocki 
907596ba34bSRafael J. Wysocki /**
908596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
909596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
910596ba34bSRafael J. Wysocki  *
911596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
912596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
913596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
914596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
915596ba34bSRafael J. Wysocki  */
916596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
917596ba34bSRafael J. Wysocki {
918596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
919596ba34bSRafael J. Wysocki 	bool run_complete;
920596ba34bSRafael J. Wysocki 
921596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
922596ba34bSRafael J. Wysocki 
923596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
924596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
925596ba34bSRafael J. Wysocki 		return;
926596ba34bSRafael J. Wysocki 
927596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
928596ba34bSRafael J. Wysocki 
929596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
930596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
931596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
932596ba34bSRafael J. Wysocki 
933596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
934596ba34bSRafael J. Wysocki 
935596ba34bSRafael J. Wysocki 	if (run_complete) {
936596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
9376f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
938596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
9396f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
940596ba34bSRafael J. Wysocki 	}
941596ba34bSRafael J. Wysocki }
942596ba34bSRafael J. Wysocki 
943596ba34bSRafael J. Wysocki #else
944596ba34bSRafael J. Wysocki 
945596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
946596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
947596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
948596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
949596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
950596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
951596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
952596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
953596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
954596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq	NULL
955596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff		NULL
956596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
957596ba34bSRafael J. Wysocki #define pm_genpd_restore		NULL
958596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
959596ba34bSRafael J. Wysocki 
960596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
961596ba34bSRafael J. Wysocki 
962f721889fSRafael J. Wysocki /**
963f721889fSRafael J. Wysocki  * pm_genpd_add_device - Add a device to an I/O PM domain.
964f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
965f721889fSRafael J. Wysocki  * @dev: Device to be added.
966f721889fSRafael J. Wysocki  */
967f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
968f721889fSRafael J. Wysocki {
969f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
970f721889fSRafael J. Wysocki 	int ret = 0;
971f721889fSRafael J. Wysocki 
972f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
973f721889fSRafael J. Wysocki 
974f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
975f721889fSRafael J. Wysocki 		return -EINVAL;
976f721889fSRafael J. Wysocki 
97717b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
978f721889fSRafael J. Wysocki 
97917b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF) {
980f721889fSRafael J. Wysocki 		ret = -EINVAL;
981f721889fSRafael J. Wysocki 		goto out;
982f721889fSRafael J. Wysocki 	}
983f721889fSRafael J. Wysocki 
984596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
985596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
986596ba34bSRafael J. Wysocki 		goto out;
987596ba34bSRafael J. Wysocki 	}
988596ba34bSRafael J. Wysocki 
989f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node)
990f721889fSRafael J. Wysocki 		if (dle->dev == dev) {
991f721889fSRafael J. Wysocki 			ret = -EINVAL;
992f721889fSRafael J. Wysocki 			goto out;
993f721889fSRafael J. Wysocki 		}
994f721889fSRafael J. Wysocki 
995f721889fSRafael J. Wysocki 	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
996f721889fSRafael J. Wysocki 	if (!dle) {
997f721889fSRafael J. Wysocki 		ret = -ENOMEM;
998f721889fSRafael J. Wysocki 		goto out;
999f721889fSRafael J. Wysocki 	}
1000f721889fSRafael J. Wysocki 
1001f721889fSRafael J. Wysocki 	dle->dev = dev;
1002f721889fSRafael J. Wysocki 	dle->need_restore = false;
1003f721889fSRafael J. Wysocki 	list_add_tail(&dle->node, &genpd->dev_list);
1004596ba34bSRafael J. Wysocki 	genpd->device_count++;
1005f721889fSRafael J. Wysocki 
1006f721889fSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1007f721889fSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
1008f721889fSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1009f721889fSRafael J. Wysocki 
1010f721889fSRafael J. Wysocki  out:
101117b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1012f721889fSRafael J. Wysocki 
1013f721889fSRafael J. Wysocki 	return ret;
1014f721889fSRafael J. Wysocki }
1015f721889fSRafael J. Wysocki 
1016f721889fSRafael J. Wysocki /**
1017f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1018f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1019f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1020f721889fSRafael J. Wysocki  */
1021f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1022f721889fSRafael J. Wysocki 			   struct device *dev)
1023f721889fSRafael J. Wysocki {
1024f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
1025f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1026f721889fSRafael J. Wysocki 
1027f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1028f721889fSRafael J. Wysocki 
1029f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1030f721889fSRafael J. Wysocki 		return -EINVAL;
1031f721889fSRafael J. Wysocki 
103217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1033f721889fSRafael J. Wysocki 
1034596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1035596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1036596ba34bSRafael J. Wysocki 		goto out;
1037596ba34bSRafael J. Wysocki 	}
1038596ba34bSRafael J. Wysocki 
1039f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node) {
1040f721889fSRafael J. Wysocki 		if (dle->dev != dev)
1041f721889fSRafael J. Wysocki 			continue;
1042f721889fSRafael J. Wysocki 
1043f721889fSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
1044f721889fSRafael J. Wysocki 		dev->pm_domain = NULL;
1045f721889fSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
1046f721889fSRafael J. Wysocki 
1047596ba34bSRafael J. Wysocki 		genpd->device_count--;
1048f721889fSRafael J. Wysocki 		list_del(&dle->node);
1049f721889fSRafael J. Wysocki 		kfree(dle);
1050f721889fSRafael J. Wysocki 
1051f721889fSRafael J. Wysocki 		ret = 0;
1052f721889fSRafael J. Wysocki 		break;
1053f721889fSRafael J. Wysocki 	}
1054f721889fSRafael J. Wysocki 
1055596ba34bSRafael J. Wysocki  out:
105617b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1057f721889fSRafael J. Wysocki 
1058f721889fSRafael J. Wysocki 	return ret;
1059f721889fSRafael J. Wysocki }
1060f721889fSRafael J. Wysocki 
1061f721889fSRafael J. Wysocki /**
1062f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1063f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1064f721889fSRafael J. Wysocki  * @new_subdomain: Subdomain to be added.
1065f721889fSRafael J. Wysocki  */
1066f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1067f721889fSRafael J. Wysocki 			   struct generic_pm_domain *new_subdomain)
1068f721889fSRafael J. Wysocki {
1069f721889fSRafael J. Wysocki 	struct generic_pm_domain *subdomain;
1070f721889fSRafael J. Wysocki 	int ret = 0;
1071f721889fSRafael J. Wysocki 
1072f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
1073f721889fSRafael J. Wysocki 		return -EINVAL;
1074f721889fSRafael J. Wysocki 
107517b75ecaSRafael J. Wysocki  start:
107617b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
107717b75ecaSRafael J. Wysocki 	mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
1078f721889fSRafael J. Wysocki 
107917b75ecaSRafael J. Wysocki 	if (new_subdomain->status != GPD_STATE_POWER_OFF
108017b75ecaSRafael J. Wysocki 	    && new_subdomain->status != GPD_STATE_ACTIVE) {
108117b75ecaSRafael J. Wysocki 		mutex_unlock(&new_subdomain->lock);
108217b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
108317b75ecaSRafael J. Wysocki 		goto start;
108417b75ecaSRafael J. Wysocki 	}
108517b75ecaSRafael J. Wysocki 
108617b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
108717b75ecaSRafael J. Wysocki 	    &&  new_subdomain->status != GPD_STATE_POWER_OFF) {
1088f721889fSRafael J. Wysocki 		ret = -EINVAL;
1089f721889fSRafael J. Wysocki 		goto out;
1090f721889fSRafael J. Wysocki 	}
1091f721889fSRafael J. Wysocki 
1092f721889fSRafael J. Wysocki 	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
1093f721889fSRafael J. Wysocki 		if (subdomain == new_subdomain) {
1094f721889fSRafael J. Wysocki 			ret = -EINVAL;
1095f721889fSRafael J. Wysocki 			goto out;
1096f721889fSRafael J. Wysocki 		}
1097f721889fSRafael J. Wysocki 	}
1098f721889fSRafael J. Wysocki 
1099f721889fSRafael J. Wysocki 	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
1100f721889fSRafael J. Wysocki 	new_subdomain->parent = genpd;
110117b75ecaSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1102f721889fSRafael J. Wysocki 		genpd->sd_count++;
1103f721889fSRafael J. Wysocki 
1104f721889fSRafael J. Wysocki  out:
110517b75ecaSRafael J. Wysocki 	mutex_unlock(&new_subdomain->lock);
110617b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1107f721889fSRafael J. Wysocki 
1108f721889fSRafael J. Wysocki 	return ret;
1109f721889fSRafael J. Wysocki }
1110f721889fSRafael J. Wysocki 
1111f721889fSRafael J. Wysocki /**
1112f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1113f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
1114f721889fSRafael J. Wysocki  * @target: Subdomain to be removed.
1115f721889fSRafael J. Wysocki  */
1116f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
1117f721889fSRafael J. Wysocki 			      struct generic_pm_domain *target)
1118f721889fSRafael J. Wysocki {
1119f721889fSRafael J. Wysocki 	struct generic_pm_domain *subdomain;
1120f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1121f721889fSRafael J. Wysocki 
1122f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
1123f721889fSRafael J. Wysocki 		return -EINVAL;
1124f721889fSRafael J. Wysocki 
112517b75ecaSRafael J. Wysocki  start:
112617b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1127f721889fSRafael J. Wysocki 
1128f721889fSRafael J. Wysocki 	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
1129f721889fSRafael J. Wysocki 		if (subdomain != target)
1130f721889fSRafael J. Wysocki 			continue;
1131f721889fSRafael J. Wysocki 
1132f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1133f721889fSRafael J. Wysocki 
113417b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
113517b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
113617b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
113717b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
113817b75ecaSRafael J. Wysocki 			goto start;
113917b75ecaSRafael J. Wysocki 		}
114017b75ecaSRafael J. Wysocki 
1141f721889fSRafael J. Wysocki 		list_del(&subdomain->sd_node);
1142f721889fSRafael J. Wysocki 		subdomain->parent = NULL;
114317b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1144f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1145f721889fSRafael J. Wysocki 
1146f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1147f721889fSRafael J. Wysocki 
1148f721889fSRafael J. Wysocki 		ret = 0;
1149f721889fSRafael J. Wysocki 		break;
1150f721889fSRafael J. Wysocki 	}
1151f721889fSRafael J. Wysocki 
115217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1153f721889fSRafael J. Wysocki 
1154f721889fSRafael J. Wysocki 	return ret;
1155f721889fSRafael J. Wysocki }
1156f721889fSRafael J. Wysocki 
1157f721889fSRafael J. Wysocki /**
1158f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1159f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1160f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1161f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1162f721889fSRafael J. Wysocki  */
1163f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1164f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1165f721889fSRafael J. Wysocki {
1166f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1167f721889fSRafael J. Wysocki 		return;
1168f721889fSRafael J. Wysocki 
1169f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->sd_node);
1170f721889fSRafael J. Wysocki 	genpd->parent = NULL;
1171f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1172f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->sd_list);
1173f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1174f721889fSRafael J. Wysocki 	genpd->gov = gov;
1175f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1176f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1177f721889fSRafael J. Wysocki 	genpd->sd_count = 0;
117817b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
117917b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1180c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1181c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1182596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1183596ba34bSRafael J. Wysocki 	genpd->suspended_count = 0;
1184f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1185f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1186f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1187596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1188596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
1189596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1190596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
1191596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1192596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
1193596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1194596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
1195596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1196596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
1197596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
1198596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
1199596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_restore;
1200596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
1201f721889fSRafael J. Wysocki }
1202