xref: /openbmc/linux/drivers/base/power/domain.c (revision cc85b207)
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>
18d5e4cbfeSRafael J. Wysocki #include <linux/export.h>
19d5e4cbfeSRafael J. Wysocki 
20d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev)		\
21d5e4cbfeSRafael J. Wysocki ({								\
22d5e4cbfeSRafael J. Wysocki 	type (*__routine)(struct device *__d); 			\
23d5e4cbfeSRafael J. Wysocki 	type __ret = (type)0;					\
24d5e4cbfeSRafael J. Wysocki 								\
25d5e4cbfeSRafael J. Wysocki 	__routine = genpd->dev_ops.callback; 			\
26d5e4cbfeSRafael J. Wysocki 	if (__routine) {					\
27d5e4cbfeSRafael J. Wysocki 		__ret = __routine(dev); 			\
28d5e4cbfeSRafael J. Wysocki 	} else {						\
29d5e4cbfeSRafael J. Wysocki 		__routine = dev_gpd_data(dev)->ops.callback;	\
30d5e4cbfeSRafael J. Wysocki 		if (__routine) 					\
31d5e4cbfeSRafael J. Wysocki 			__ret = __routine(dev);			\
32d5e4cbfeSRafael J. Wysocki 	}							\
33d5e4cbfeSRafael J. Wysocki 	__ret;							\
34d5e4cbfeSRafael J. Wysocki })
35f721889fSRafael J. Wysocki 
360140d8bdSRafael J. Wysocki #define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name)	\
370140d8bdSRafael J. Wysocki ({										\
380140d8bdSRafael J. Wysocki 	ktime_t __start = ktime_get();						\
390140d8bdSRafael J. Wysocki 	type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);		\
400140d8bdSRafael J. Wysocki 	s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));		\
410140d8bdSRafael J. Wysocki 	struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev);		\
420140d8bdSRafael J. Wysocki 	if (__elapsed > __gpd_data->td.field) {					\
430140d8bdSRafael J. Wysocki 		__gpd_data->td.field = __elapsed;				\
440140d8bdSRafael J. Wysocki 		dev_warn(dev, name " latency exceeded, new value %lld ns\n",	\
450140d8bdSRafael J. Wysocki 			__elapsed);						\
460140d8bdSRafael J. Wysocki 	}									\
470140d8bdSRafael J. Wysocki 	__retval;								\
480140d8bdSRafael J. Wysocki })
490140d8bdSRafael J. Wysocki 
505125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list);
515125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock);
525125bbf3SRafael J. Wysocki 
535248051bSRafael J. Wysocki #ifdef CONFIG_PM
545248051bSRafael J. Wysocki 
55b02c999aSRafael J. Wysocki struct generic_pm_domain *dev_to_genpd(struct device *dev)
565248051bSRafael J. Wysocki {
575248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
585248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
595248051bSRafael J. Wysocki 
60596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
615248051bSRafael J. Wysocki }
62f721889fSRafael J. Wysocki 
63d5e4cbfeSRafael J. Wysocki static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
64d5e4cbfeSRafael J. Wysocki {
650140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev,
660140d8bdSRafael J. Wysocki 					stop_latency_ns, "stop");
67d5e4cbfeSRafael J. Wysocki }
68d5e4cbfeSRafael J. Wysocki 
69d5e4cbfeSRafael J. Wysocki static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
70d5e4cbfeSRafael J. Wysocki {
710140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev,
720140d8bdSRafael J. Wysocki 					start_latency_ns, "start");
73d5e4cbfeSRafael J. Wysocki }
74d5e4cbfeSRafael J. Wysocki 
75ecf00475SRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev)
76ecf00475SRafael J. Wysocki {
770140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev,
780140d8bdSRafael J. Wysocki 					save_state_latency_ns, "state save");
79ecf00475SRafael J. Wysocki }
80ecf00475SRafael J. Wysocki 
81ecf00475SRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev)
82ecf00475SRafael J. Wysocki {
830140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev,
840140d8bdSRafael J. Wysocki 					restore_state_latency_ns,
850140d8bdSRafael J. Wysocki 					"state restore");
86ecf00475SRafael J. Wysocki }
87ecf00475SRafael J. Wysocki 
88c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
89f721889fSRafael J. Wysocki {
90c4bb3160SRafael J. Wysocki 	bool ret = false;
91c4bb3160SRafael J. Wysocki 
92c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
93c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
94c4bb3160SRafael J. Wysocki 
95c4bb3160SRafael J. Wysocki 	return ret;
96c4bb3160SRafael J. Wysocki }
97c4bb3160SRafael J. Wysocki 
98c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
99c4bb3160SRafael J. Wysocki {
100c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
101c4bb3160SRafael J. Wysocki 	smp_mb__after_atomic_inc();
102f721889fSRafael J. Wysocki }
103f721889fSRafael J. Wysocki 
10417b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
10517b75ecaSRafael J. Wysocki {
10617b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
10717b75ecaSRafael J. Wysocki 
10817b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
10917b75ecaSRafael J. Wysocki 	/*
11017b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
11117b75ecaSRafael J. Wysocki 	 * or the power off state.
11217b75ecaSRafael J. Wysocki 	 */
11317b75ecaSRafael J. Wysocki 	for (;;) {
11417b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
11517b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
116c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
117c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
11817b75ecaSRafael J. Wysocki 			break;
11917b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
12017b75ecaSRafael J. Wysocki 
12117b75ecaSRafael J. Wysocki 		schedule();
12217b75ecaSRafael J. Wysocki 
12317b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
12417b75ecaSRafael J. Wysocki 	}
12517b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
12617b75ecaSRafael J. Wysocki }
12717b75ecaSRafael J. Wysocki 
12817b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
12917b75ecaSRafael J. Wysocki {
13017b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
13117b75ecaSRafael J. Wysocki }
13217b75ecaSRafael J. Wysocki 
133c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
134c6d22b37SRafael J. Wysocki {
135c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
136c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
137c6d22b37SRafael J. Wysocki }
138c6d22b37SRafael J. Wysocki 
139f721889fSRafael J. Wysocki /**
1405063ce15SRafael J. Wysocki  * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
1415248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
1425248051bSRafael J. Wysocki  *
1435063ce15SRafael J. Wysocki  * Restore power to @genpd and all of its masters so that it is possible to
1445248051bSRafael J. Wysocki  * resume a device belonging to it.
1455248051bSRafael J. Wysocki  */
1463f241775SRafael J. Wysocki int __pm_genpd_poweron(struct generic_pm_domain *genpd)
1473f241775SRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
1485248051bSRafael J. Wysocki {
1495063ce15SRafael J. Wysocki 	struct gpd_link *link;
1503f241775SRafael J. Wysocki 	DEFINE_WAIT(wait);
1515248051bSRafael J. Wysocki 	int ret = 0;
1525248051bSRafael J. Wysocki 
1535063ce15SRafael J. Wysocki 	/* If the domain's master is being waited for, we have to wait too. */
1543f241775SRafael J. Wysocki 	for (;;) {
1553f241775SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
1563f241775SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
15717877eb5SRafael J. Wysocki 		if (genpd->status != GPD_STATE_WAIT_MASTER)
1583f241775SRafael J. Wysocki 			break;
1593f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1603f241775SRafael J. Wysocki 
1613f241775SRafael J. Wysocki 		schedule();
1623f241775SRafael J. Wysocki 
16317b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
1643f241775SRafael J. Wysocki 	}
1653f241775SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
16617b75ecaSRafael J. Wysocki 
16717b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
168596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
1693f241775SRafael J. Wysocki 		return 0;
1705248051bSRafael J. Wysocki 
171c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
172c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
1733f241775SRafael J. Wysocki 		return 0;
174c6d22b37SRafael J. Wysocki 	}
175c6d22b37SRafael J. Wysocki 
1765063ce15SRafael J. Wysocki 	/*
1775063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
1785063ce15SRafael J. Wysocki 	 * executed, unless one of the masters' .power_on() callbacks fiddles
1795063ce15SRafael J. Wysocki 	 * with it.
1805063ce15SRafael J. Wysocki 	 */
1815063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1825063ce15SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
18317877eb5SRafael J. Wysocki 		genpd->status = GPD_STATE_WAIT_MASTER;
1843c07cbc4SRafael J. Wysocki 
1855248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1865248051bSRafael J. Wysocki 
1875063ce15SRafael J. Wysocki 		ret = pm_genpd_poweron(link->master);
1889e08cf42SRafael J. Wysocki 
1899e08cf42SRafael J. Wysocki 		mutex_lock(&genpd->lock);
1909e08cf42SRafael J. Wysocki 
1913f241775SRafael J. Wysocki 		/*
1923f241775SRafael J. Wysocki 		 * The "wait for parent" status is guaranteed not to change
1935063ce15SRafael J. Wysocki 		 * while the master is powering on.
1943f241775SRafael J. Wysocki 		 */
1953f241775SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1963f241775SRafael J. Wysocki 		wake_up_all(&genpd->status_wait_queue);
1975063ce15SRafael J. Wysocki 		if (ret) {
1985063ce15SRafael J. Wysocki 			genpd_sd_counter_dec(link->master);
1999e08cf42SRafael J. Wysocki 			goto err;
2005248051bSRafael J. Wysocki 		}
2015063ce15SRafael J. Wysocki 	}
2025248051bSRafael J. Wysocki 
2039e08cf42SRafael J. Wysocki 	if (genpd->power_on) {
2040140d8bdSRafael J. Wysocki 		ktime_t time_start = ktime_get();
2050140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
2060140d8bdSRafael J. Wysocki 
207fe202fdeSRafael J. Wysocki 		ret = genpd->power_on(genpd);
2089e08cf42SRafael J. Wysocki 		if (ret)
2099e08cf42SRafael J. Wysocki 			goto err;
2100140d8bdSRafael J. Wysocki 
2110140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
212e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_on_latency_ns) {
2130140d8bdSRafael J. Wysocki 			genpd->power_on_latency_ns = elapsed_ns;
214e84b2c20SRafael J. Wysocki 			if (genpd->name)
215e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-on latency exceeded, "
216e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
217e84b2c20SRafael J. Wysocki 					elapsed_ns);
218e84b2c20SRafael J. Wysocki 		}
2193c07cbc4SRafael J. Wysocki 	}
2205248051bSRafael J. Wysocki 
2219e08cf42SRafael J. Wysocki 	genpd_set_active(genpd);
2229e08cf42SRafael J. Wysocki 
2233f241775SRafael J. Wysocki 	return 0;
2249e08cf42SRafael J. Wysocki 
2259e08cf42SRafael J. Wysocki  err:
2265063ce15SRafael J. Wysocki 	list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
2275063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
2289e08cf42SRafael J. Wysocki 
2293f241775SRafael J. Wysocki 	return ret;
2303f241775SRafael J. Wysocki }
2313f241775SRafael J. Wysocki 
2323f241775SRafael J. Wysocki /**
2335063ce15SRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its masters.
2343f241775SRafael J. Wysocki  * @genpd: PM domain to power up.
2353f241775SRafael J. Wysocki  */
2363f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd)
2373f241775SRafael J. Wysocki {
2383f241775SRafael J. Wysocki 	int ret;
2393f241775SRafael J. Wysocki 
2403f241775SRafael J. Wysocki 	mutex_lock(&genpd->lock);
2413f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
2423f241775SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
2433f241775SRafael J. Wysocki 	return ret;
2445248051bSRafael J. Wysocki }
2455248051bSRafael J. Wysocki 
2465248051bSRafael J. Wysocki #endif /* CONFIG_PM */
2475248051bSRafael J. Wysocki 
2485248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
2495248051bSRafael J. Wysocki 
2505248051bSRafael J. Wysocki /**
251f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
2524605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to save the state of.
253f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
254f721889fSRafael J. Wysocki  */
2554605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd,
256f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
25717b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
258f721889fSRafael J. Wysocki {
259cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
2604605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
261f721889fSRafael J. Wysocki 	int ret = 0;
262f721889fSRafael J. Wysocki 
263cd0ea672SRafael J. Wysocki 	if (gpd_data->need_restore)
264f721889fSRafael J. Wysocki 		return 0;
265f721889fSRafael J. Wysocki 
26617b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
26717b75ecaSRafael J. Wysocki 
268d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
269ecf00475SRafael J. Wysocki 	ret = genpd_save_dev(genpd, dev);
270d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
271f721889fSRafael J. Wysocki 
27217b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
27317b75ecaSRafael J. Wysocki 
274f721889fSRafael J. Wysocki 	if (!ret)
275cd0ea672SRafael J. Wysocki 		gpd_data->need_restore = true;
276f721889fSRafael J. Wysocki 
277f721889fSRafael J. Wysocki 	return ret;
278f721889fSRafael J. Wysocki }
279f721889fSRafael J. Wysocki 
280f721889fSRafael J. Wysocki /**
281f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
2824605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to restore the state of.
283f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
284f721889fSRafael J. Wysocki  */
2854605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
286f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
28717b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
288f721889fSRafael J. Wysocki {
289cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
2904605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
291f721889fSRafael J. Wysocki 
292cd0ea672SRafael J. Wysocki 	if (!gpd_data->need_restore)
293f721889fSRafael J. Wysocki 		return;
294f721889fSRafael J. Wysocki 
29517b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
29617b75ecaSRafael J. Wysocki 
297d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
298ecf00475SRafael J. Wysocki 	genpd_restore_dev(genpd, dev);
299d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
300f721889fSRafael J. Wysocki 
30117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
30217b75ecaSRafael J. Wysocki 
303cd0ea672SRafael J. Wysocki 	gpd_data->need_restore = false;
304f721889fSRafael J. Wysocki }
305f721889fSRafael J. Wysocki 
306f721889fSRafael J. Wysocki /**
307c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
308c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
309c6d22b37SRafael J. Wysocki  *
310c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
311c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
312c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
313c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
314c6d22b37SRafael J. Wysocki  */
315c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
316c6d22b37SRafael J. Wysocki {
31717877eb5SRafael J. Wysocki 	return genpd->status == GPD_STATE_WAIT_MASTER
3183f241775SRafael J. Wysocki 		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
319c6d22b37SRafael J. Wysocki }
320c6d22b37SRafael J. Wysocki 
321c6d22b37SRafael J. Wysocki /**
32256375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
32356375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
32456375fd4SRafael J. Wysocki  *
32556375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
32656375fd4SRafael J. Wysocki  * before.
32756375fd4SRafael J. Wysocki  */
3280bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
32956375fd4SRafael J. Wysocki {
33056375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
33156375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
33256375fd4SRafael J. Wysocki }
33356375fd4SRafael J. Wysocki 
33456375fd4SRafael J. Wysocki /**
335f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
336f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
337f721889fSRafael J. Wysocki  *
338f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
339f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
340f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
341f721889fSRafael J. Wysocki  */
342f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
34317b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
344f721889fSRafael J. Wysocki {
3454605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
3465063ce15SRafael J. Wysocki 	struct gpd_link *link;
347f721889fSRafael J. Wysocki 	unsigned int not_suspended;
348c6d22b37SRafael J. Wysocki 	int ret = 0;
349f721889fSRafael J. Wysocki 
350c6d22b37SRafael J. Wysocki  start:
351c6d22b37SRafael J. Wysocki 	/*
352c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
353c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
3545063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
355c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
3563f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
357c6d22b37SRafael J. Wysocki 	 */
3583f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
35917877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
3603f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
361f721889fSRafael J. Wysocki 		return 0;
362f721889fSRafael J. Wysocki 
363c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
364f721889fSRafael J. Wysocki 		return -EBUSY;
365f721889fSRafael J. Wysocki 
366f721889fSRafael J. Wysocki 	not_suspended = 0;
3674605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
3680aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
3690aa2a221SRafael J. Wysocki 		    || pdd->dev->power.irq_safe))
370f721889fSRafael J. Wysocki 			not_suspended++;
371f721889fSRafael J. Wysocki 
372f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
373f721889fSRafael J. Wysocki 		return -EBUSY;
374f721889fSRafael J. Wysocki 
375c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
376c6d22b37SRafael J. Wysocki 		/*
377c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
378c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
379c6d22b37SRafael J. Wysocki 		 */
380c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
381c6d22b37SRafael J. Wysocki 		return 0;
382c6d22b37SRafael J. Wysocki 	}
383c6d22b37SRafael J. Wysocki 
384f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
385f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
386f721889fSRafael J. Wysocki 			return -EAGAIN;
387f721889fSRafael J. Wysocki 	}
388f721889fSRafael J. Wysocki 
38917b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
390c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
39117b75ecaSRafael J. Wysocki 
3924605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
3933c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
3944605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
3953f241775SRafael J. Wysocki 
3963f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
3973f241775SRafael J. Wysocki 			goto out;
3983f241775SRafael J. Wysocki 
399697a7f37SRafael J. Wysocki 		if (ret) {
400697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
401697a7f37SRafael J. Wysocki 			goto out;
402697a7f37SRafael J. Wysocki 		}
403f721889fSRafael J. Wysocki 
404c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
405c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
406c6d22b37SRafael J. Wysocki 			goto start;
407c6d22b37SRafael J. Wysocki 		}
408c6d22b37SRafael J. Wysocki 	}
40917b75ecaSRafael J. Wysocki 
4103c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
4110140d8bdSRafael J. Wysocki 		ktime_t time_start;
4120140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
4130140d8bdSRafael J. Wysocki 
4143c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
4153c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
416c6d22b37SRafael J. Wysocki 			goto out;
417c6d22b37SRafael J. Wysocki 		}
41817b75ecaSRafael J. Wysocki 
4190140d8bdSRafael J. Wysocki 		time_start = ktime_get();
4200140d8bdSRafael J. Wysocki 
4213c07cbc4SRafael J. Wysocki 		/*
4225063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
4235063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
4243c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
4253c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
4263c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
4273c07cbc4SRafael J. Wysocki 		 * happen very often).
4283c07cbc4SRafael J. Wysocki 		 */
429d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
430d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
431d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
432d2805402SRafael J. Wysocki 			goto out;
433d2805402SRafael J. Wysocki 		}
4340140d8bdSRafael J. Wysocki 
4350140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
436e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_off_latency_ns) {
4370140d8bdSRafael J. Wysocki 			genpd->power_off_latency_ns = elapsed_ns;
438e84b2c20SRafael J. Wysocki 			if (genpd->name)
439e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-off latency exceeded, "
440e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
441e84b2c20SRafael J. Wysocki 					elapsed_ns);
442e84b2c20SRafael J. Wysocki 		}
443d2805402SRafael J. Wysocki 	}
444f721889fSRafael J. Wysocki 
44517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
446221e9b58SRafael J. Wysocki 	genpd->power_off_time = ktime_get();
447221e9b58SRafael J. Wysocki 
448221e9b58SRafael J. Wysocki 	/* Update PM QoS information for devices in the domain. */
449221e9b58SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
450221e9b58SRafael J. Wysocki 		struct gpd_timing_data *td = &to_gpd_data(pdd)->td;
451221e9b58SRafael J. Wysocki 
452221e9b58SRafael J. Wysocki 		pm_runtime_update_max_time_suspended(pdd->dev,
453221e9b58SRafael J. Wysocki 					td->start_latency_ns +
454221e9b58SRafael J. Wysocki 					td->restore_state_latency_ns +
455221e9b58SRafael J. Wysocki 					genpd->power_on_latency_ns);
456221e9b58SRafael J. Wysocki 	}
457f721889fSRafael J. Wysocki 
4585063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
4595063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
4605063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
4615063ce15SRafael J. Wysocki 	}
46217b75ecaSRafael J. Wysocki 
463c6d22b37SRafael J. Wysocki  out:
464c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
465c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
466c6d22b37SRafael J. Wysocki 	return ret;
467f721889fSRafael J. Wysocki }
468f721889fSRafael J. Wysocki 
469f721889fSRafael J. Wysocki /**
470f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
471f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
472f721889fSRafael J. Wysocki  */
473f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
474f721889fSRafael J. Wysocki {
475f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
476f721889fSRafael J. Wysocki 
477f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
478f721889fSRafael J. Wysocki 
47917b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
480f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
48117b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
482f721889fSRafael J. Wysocki }
483f721889fSRafael J. Wysocki 
484f721889fSRafael J. Wysocki /**
485f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
486f721889fSRafael J. Wysocki  * @dev: Device to suspend.
487f721889fSRafael J. Wysocki  *
488f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
489f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
490f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
491f721889fSRafael J. Wysocki  */
492f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
493f721889fSRafael J. Wysocki {
494f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
495b02c999aSRafael J. Wysocki 	bool (*stop_ok)(struct device *__dev);
496d5e4cbfeSRafael J. Wysocki 	int ret;
497f721889fSRafael J. Wysocki 
498f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
499f721889fSRafael J. Wysocki 
5005248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5015248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
502f721889fSRafael J. Wysocki 		return -EINVAL;
503f721889fSRafael J. Wysocki 
5040aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5050aa2a221SRafael J. Wysocki 
506b02c999aSRafael J. Wysocki 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
507b02c999aSRafael J. Wysocki 	if (stop_ok && !stop_ok(dev))
508b02c999aSRafael J. Wysocki 		return -EBUSY;
509b02c999aSRafael J. Wysocki 
510d5e4cbfeSRafael J. Wysocki 	ret = genpd_stop_dev(genpd, dev);
511f721889fSRafael J. Wysocki 	if (ret)
51217b75ecaSRafael J. Wysocki 		return ret;
51317b75ecaSRafael J. Wysocki 
514b02c999aSRafael J. Wysocki 	pm_runtime_update_max_time_suspended(dev,
515b02c999aSRafael J. Wysocki 				dev_gpd_data(dev)->td.start_latency_ns);
516b02c999aSRafael J. Wysocki 
5170aa2a221SRafael J. Wysocki 	/*
5180aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
5190aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
5200aa2a221SRafael J. Wysocki 	 */
5210aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
5220aa2a221SRafael J. Wysocki 		return 0;
5230aa2a221SRafael J. Wysocki 
524c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
525f721889fSRafael J. Wysocki 	genpd->in_progress++;
526f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
527f721889fSRafael J. Wysocki 	genpd->in_progress--;
528c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
529f721889fSRafael J. Wysocki 
530f721889fSRafael J. Wysocki 	return 0;
531f721889fSRafael J. Wysocki }
532f721889fSRafael J. Wysocki 
533f721889fSRafael J. Wysocki /**
534f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
535f721889fSRafael J. Wysocki  * @dev: Device to resume.
536f721889fSRafael J. Wysocki  *
537f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
538f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
539f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
540f721889fSRafael J. Wysocki  */
541f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
542f721889fSRafael J. Wysocki {
543f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
544c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
545f721889fSRafael J. Wysocki 	int ret;
546f721889fSRafael J. Wysocki 
547f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
548f721889fSRafael J. Wysocki 
5495248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5505248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
551f721889fSRafael J. Wysocki 		return -EINVAL;
552f721889fSRafael J. Wysocki 
5530aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5540aa2a221SRafael J. Wysocki 
5550aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
5560aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
5570aa2a221SRafael J. Wysocki 		goto out;
5580aa2a221SRafael J. Wysocki 
559c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
5603f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
5613f241775SRafael J. Wysocki 	if (ret) {
5623f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
5633f241775SRafael J. Wysocki 		return ret;
5643f241775SRafael J. Wysocki 	}
56517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
566c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
567c6d22b37SRafael J. Wysocki 	for (;;) {
568c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
569c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
570c6d22b37SRafael J. Wysocki 		/*
571c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
572c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
573c6d22b37SRafael J. Wysocki 		 * not wait.
574c6d22b37SRafael J. Wysocki 		 */
575c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
576c6d22b37SRafael J. Wysocki 			break;
577c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
578c6d22b37SRafael J. Wysocki 
579c6d22b37SRafael J. Wysocki 		schedule();
580c6d22b37SRafael J. Wysocki 
581c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
582c6d22b37SRafael J. Wysocki 	}
583c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
584cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
585c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
586c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
58717b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
588c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
58917b75ecaSRafael J. Wysocki 
5900aa2a221SRafael J. Wysocki  out:
591d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
592f721889fSRafael J. Wysocki 
593f721889fSRafael J. Wysocki 	return 0;
594f721889fSRafael J. Wysocki }
595f721889fSRafael J. Wysocki 
59617f2ae7fSRafael J. Wysocki /**
59717f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
59817f2ae7fSRafael J. Wysocki  */
59917f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
60017f2ae7fSRafael J. Wysocki {
60117f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
60217f2ae7fSRafael J. Wysocki 
60317f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
60417f2ae7fSRafael J. Wysocki 
60517f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
60617f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
60717f2ae7fSRafael J. Wysocki 
60817f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
60917f2ae7fSRafael J. Wysocki }
61017f2ae7fSRafael J. Wysocki 
611f721889fSRafael J. Wysocki #else
612f721889fSRafael J. Wysocki 
613f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
614f721889fSRafael J. Wysocki 
615f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
616f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
617f721889fSRafael J. Wysocki 
618f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
619f721889fSRafael J. Wysocki 
620596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
621596ba34bSRafael J. Wysocki 
622d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
623d5e4cbfeSRafael J. Wysocki 				    struct device *dev)
624d5e4cbfeSRafael J. Wysocki {
625d5e4cbfeSRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
626d5e4cbfeSRafael J. Wysocki }
627d5e4cbfeSRafael J. Wysocki 
628d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
629d23b9b00SRafael J. Wysocki {
630d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
631d23b9b00SRafael J. Wysocki }
632d23b9b00SRafael J. Wysocki 
633d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
634d23b9b00SRafael J. Wysocki {
635d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
636d23b9b00SRafael J. Wysocki }
637d23b9b00SRafael J. Wysocki 
638d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
639d23b9b00SRafael J. Wysocki {
640d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
641d23b9b00SRafael J. Wysocki }
642d23b9b00SRafael J. Wysocki 
643d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
644d23b9b00SRafael J. Wysocki {
645d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
646d23b9b00SRafael J. Wysocki }
647d23b9b00SRafael J. Wysocki 
648d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
649d23b9b00SRafael J. Wysocki {
650d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
651d23b9b00SRafael J. Wysocki }
652d23b9b00SRafael J. Wysocki 
653d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
654d23b9b00SRafael J. Wysocki {
655d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
656d23b9b00SRafael J. Wysocki }
657d23b9b00SRafael J. Wysocki 
658d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
659d23b9b00SRafael J. Wysocki {
660d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
661d23b9b00SRafael J. Wysocki }
662d23b9b00SRafael J. Wysocki 
663d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
664d23b9b00SRafael J. Wysocki {
665d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
666d23b9b00SRafael J. Wysocki }
667d23b9b00SRafael J. Wysocki 
668596ba34bSRafael J. Wysocki /**
6695063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
670596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
671596ba34bSRafael J. Wysocki  *
672596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
6735063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
674596ba34bSRafael J. Wysocki  *
675596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
676596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
677596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
678596ba34bSRafael J. Wysocki  */
679596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
680596ba34bSRafael J. Wysocki {
6815063ce15SRafael J. Wysocki 	struct gpd_link *link;
682596ba34bSRafael J. Wysocki 
68317b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
684596ba34bSRafael J. Wysocki 		return;
685596ba34bSRafael J. Wysocki 
686c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
687c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
688596ba34bSRafael J. Wysocki 		return;
689596ba34bSRafael J. Wysocki 
690596ba34bSRafael J. Wysocki 	if (genpd->power_off)
691596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
692596ba34bSRafael J. Wysocki 
69317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
6945063ce15SRafael J. Wysocki 
6955063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
6965063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
6975063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
698596ba34bSRafael J. Wysocki 	}
699596ba34bSRafael J. Wysocki }
700596ba34bSRafael J. Wysocki 
701596ba34bSRafael J. Wysocki /**
7024ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
7034ecd6e65SRafael J. Wysocki  * @dev: Device to check.
7044ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
7054ecd6e65SRafael J. Wysocki  *
7064ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
7074ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
7084ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
7094ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
7104ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
7114ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
7124ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
7134ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
7144ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
7154ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
7164ecd6e65SRafael J. Wysocki  */
7174ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
7184ecd6e65SRafael J. Wysocki {
7194ecd6e65SRafael J. Wysocki 	bool active_wakeup;
7204ecd6e65SRafael J. Wysocki 
7214ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
7224ecd6e65SRafael J. Wysocki 		return false;
7234ecd6e65SRafael J. Wysocki 
724d5e4cbfeSRafael J. Wysocki 	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
7254ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
7264ecd6e65SRafael J. Wysocki }
7274ecd6e65SRafael J. Wysocki 
7284ecd6e65SRafael J. Wysocki /**
729596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
730596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
731596ba34bSRafael J. Wysocki  *
732596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
733596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
734596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
735596ba34bSRafael J. Wysocki  * consisting of I/O devices.
736596ba34bSRafael J. Wysocki  */
737596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
738596ba34bSRafael J. Wysocki {
739596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
740b6c10c84SRafael J. Wysocki 	int ret;
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 
74817b75ecaSRafael J. Wysocki 	/*
74917b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
75017b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
75117b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
75217b75ecaSRafael J. Wysocki 	 */
75317b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
75417b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
75517b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
75617b75ecaSRafael J. Wysocki 
75717b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
75817b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
75917b75ecaSRafael J. Wysocki 		return -EBUSY;
76017b75ecaSRafael J. Wysocki 	}
76117b75ecaSRafael J. Wysocki 
7624ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
7634ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
7644ecd6e65SRafael J. Wysocki 
76517b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
766596ba34bSRafael J. Wysocki 
767596ba34bSRafael J. Wysocki 	if (genpd->prepared_count++ == 0)
76817b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
76917b75ecaSRafael J. Wysocki 
77017b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
771596ba34bSRafael J. Wysocki 
772596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
77317b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
774596ba34bSRafael J. Wysocki 		return 0;
775596ba34bSRafael J. Wysocki 	}
776596ba34bSRafael J. Wysocki 
777596ba34bSRafael J. Wysocki 	/*
77817b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
77917b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
780d5e4cbfeSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
78117b75ecaSRafael J. Wysocki 	 * to make it operational.
782596ba34bSRafael J. Wysocki 	 */
78317b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
784596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
785596ba34bSRafael J. Wysocki 
786b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
787b6c10c84SRafael J. Wysocki 	if (ret) {
788b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
789b6c10c84SRafael J. Wysocki 
790b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
791b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
792b6c10c84SRafael J. Wysocki 
793b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
79417b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
795b6c10c84SRafael J. Wysocki 	}
79617b75ecaSRafael J. Wysocki 
79717b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
798b6c10c84SRafael J. Wysocki 	return ret;
799596ba34bSRafael J. Wysocki }
800596ba34bSRafael J. Wysocki 
801596ba34bSRafael J. Wysocki /**
802596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
803596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
804596ba34bSRafael J. Wysocki  *
805596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
806596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
807596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
808596ba34bSRafael J. Wysocki  */
809596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
810596ba34bSRafael J. Wysocki {
811596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
812596ba34bSRafael J. Wysocki 
813596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
814596ba34bSRafael J. Wysocki 
815596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
816596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
817596ba34bSRafael J. Wysocki 		return -EINVAL;
818596ba34bSRafael J. Wysocki 
819d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
820596ba34bSRafael J. Wysocki }
821596ba34bSRafael J. Wysocki 
822596ba34bSRafael J. Wysocki /**
8230496c8aeSRafael J. Wysocki  * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
824596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
825596ba34bSRafael J. Wysocki  *
826596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
827596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
828596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
829596ba34bSRafael J. Wysocki  */
8300496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev)
831596ba34bSRafael J. Wysocki {
832596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
833596ba34bSRafael J. Wysocki 
834596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
835596ba34bSRafael J. Wysocki 
836596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
837596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
838596ba34bSRafael J. Wysocki 		return -EINVAL;
839596ba34bSRafael J. Wysocki 
8400496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
8410496c8aeSRafael J. Wysocki }
842596ba34bSRafael J. Wysocki 
8430496c8aeSRafael J. Wysocki /**
8440496c8aeSRafael J. Wysocki  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
8450496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
8460496c8aeSRafael J. Wysocki  *
8470496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
8480496c8aeSRafael J. Wysocki  * been stopped.
8490496c8aeSRafael J. Wysocki  */
8500496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
8510496c8aeSRafael J. Wysocki {
8520496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
853596ba34bSRafael J. Wysocki 
8540496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
8550496c8aeSRafael J. Wysocki 
8560496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
8570496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
8580496c8aeSRafael J. Wysocki 		return -EINVAL;
8590496c8aeSRafael J. Wysocki 
8600496c8aeSRafael J. Wysocki 	if (genpd->suspend_power_off
8610496c8aeSRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
862d4f2d87aSRafael J. Wysocki 		return 0;
863d4f2d87aSRafael J. Wysocki 
864d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
865596ba34bSRafael J. Wysocki 
866596ba34bSRafael J. Wysocki 	/*
867596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
868596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
869596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
870596ba34bSRafael J. Wysocki 	 */
871596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
872596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
873596ba34bSRafael J. Wysocki 
874596ba34bSRafael J. Wysocki 	return 0;
875596ba34bSRafael J. Wysocki }
876596ba34bSRafael J. Wysocki 
877596ba34bSRafael J. Wysocki /**
8780496c8aeSRafael J. Wysocki  * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
879596ba34bSRafael J. Wysocki  * @dev: Device to resume.
880596ba34bSRafael J. Wysocki  *
8810496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
882596ba34bSRafael J. Wysocki  */
883596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
884596ba34bSRafael J. Wysocki {
885596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
886596ba34bSRafael J. Wysocki 
887596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
888596ba34bSRafael J. Wysocki 
889596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
890596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
891596ba34bSRafael J. Wysocki 		return -EINVAL;
892596ba34bSRafael J. Wysocki 
893cc85b207SRafael J. Wysocki 	if (genpd->suspend_power_off
894cc85b207SRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
895596ba34bSRafael J. Wysocki 		return 0;
896596ba34bSRafael J. Wysocki 
897596ba34bSRafael J. Wysocki 	/*
898596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
899596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
900596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
901596ba34bSRafael J. Wysocki 	 */
902596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
903596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
904596ba34bSRafael J. Wysocki 
9050496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
906596ba34bSRafael J. Wysocki }
907596ba34bSRafael J. Wysocki 
908596ba34bSRafael J. Wysocki /**
9090496c8aeSRafael J. Wysocki  * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
9100496c8aeSRafael J. Wysocki  * @dev: Device to resume.
9110496c8aeSRafael J. Wysocki  *
9120496c8aeSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
9130496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
9140496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
9150496c8aeSRafael J. Wysocki  * devices.
9160496c8aeSRafael J. Wysocki  */
9170496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev)
9180496c8aeSRafael J. Wysocki {
9190496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
9200496c8aeSRafael J. Wysocki 
9210496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
9220496c8aeSRafael J. Wysocki 
9230496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9240496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9250496c8aeSRafael J. Wysocki 		return -EINVAL;
9260496c8aeSRafael J. Wysocki 
9270496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
9280496c8aeSRafael J. Wysocki }
9290496c8aeSRafael J. Wysocki 
9300496c8aeSRafael J. Wysocki /**
9310496c8aeSRafael J. Wysocki  * pm_genpd_resume - Resume of device in an I/O PM domain.
932596ba34bSRafael J. Wysocki  * @dev: Device to resume.
933596ba34bSRafael J. Wysocki  *
934596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
935596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
936596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
937596ba34bSRafael J. Wysocki  */
938596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
939596ba34bSRafael J. Wysocki {
940596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
941596ba34bSRafael J. Wysocki 
942596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
943596ba34bSRafael J. Wysocki 
944596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
945596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
946596ba34bSRafael J. Wysocki 		return -EINVAL;
947596ba34bSRafael J. Wysocki 
948d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
949596ba34bSRafael J. Wysocki }
950596ba34bSRafael J. Wysocki 
951596ba34bSRafael J. Wysocki /**
9520496c8aeSRafael J. Wysocki  * pm_genpd_freeze - Freezing a device in an I/O PM domain.
953596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
954596ba34bSRafael J. Wysocki  *
955596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
956596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
957596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
958596ba34bSRafael J. Wysocki  */
959596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
960596ba34bSRafael J. Wysocki {
961596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
962596ba34bSRafael J. Wysocki 
963596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
964596ba34bSRafael J. Wysocki 
965596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
966596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
967596ba34bSRafael J. Wysocki 		return -EINVAL;
968596ba34bSRafael J. Wysocki 
969d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
970596ba34bSRafael J. Wysocki }
971596ba34bSRafael J. Wysocki 
972596ba34bSRafael J. Wysocki /**
9730496c8aeSRafael J. Wysocki  * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
9740496c8aeSRafael J. Wysocki  * @dev: Device to freeze.
9750496c8aeSRafael J. Wysocki  *
9760496c8aeSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
9770496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
9780496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
9790496c8aeSRafael J. Wysocki  * devices.
9800496c8aeSRafael J. Wysocki  */
9810496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev)
9820496c8aeSRafael J. Wysocki {
9830496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
9840496c8aeSRafael J. Wysocki 
9850496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
9860496c8aeSRafael J. Wysocki 
9870496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9880496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9890496c8aeSRafael J. Wysocki 		return -EINVAL;
9900496c8aeSRafael J. Wysocki 
9910496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
9920496c8aeSRafael J. Wysocki }
9930496c8aeSRafael J. Wysocki 
9940496c8aeSRafael J. Wysocki /**
9950496c8aeSRafael J. Wysocki  * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
996596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
997596ba34bSRafael J. Wysocki  *
998596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
999596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1000596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1001596ba34bSRafael J. Wysocki  * devices.
1002596ba34bSRafael J. Wysocki  */
1003596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
1004596ba34bSRafael J. Wysocki {
1005596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1006596ba34bSRafael J. Wysocki 
1007596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1008596ba34bSRafael J. Wysocki 
1009596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1010596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1011596ba34bSRafael J. Wysocki 		return -EINVAL;
1012596ba34bSRafael J. Wysocki 
10130496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
1014596ba34bSRafael J. Wysocki }
1015596ba34bSRafael J. Wysocki 
1016596ba34bSRafael J. Wysocki /**
10170496c8aeSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1018596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1019596ba34bSRafael J. Wysocki  *
10200496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
10210496c8aeSRafael J. Wysocki  * before the system transition.
1022596ba34bSRafael J. Wysocki  */
1023596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
1024596ba34bSRafael J. Wysocki {
1025596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1026596ba34bSRafael J. Wysocki 
1027596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1028596ba34bSRafael J. Wysocki 
1029596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1030596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1031596ba34bSRafael J. Wysocki 		return -EINVAL;
1032596ba34bSRafael J. Wysocki 
10330496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
10340496c8aeSRafael J. Wysocki }
1035596ba34bSRafael J. Wysocki 
10360496c8aeSRafael J. Wysocki /**
10370496c8aeSRafael J. Wysocki  * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
10380496c8aeSRafael J. Wysocki  * @dev: Device to thaw.
10390496c8aeSRafael J. Wysocki  *
10400496c8aeSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
10410496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10420496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10430496c8aeSRafael J. Wysocki  * devices.
10440496c8aeSRafael J. Wysocki  */
10450496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev)
10460496c8aeSRafael J. Wysocki {
10470496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1048596ba34bSRafael J. Wysocki 
10490496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10500496c8aeSRafael J. Wysocki 
10510496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10520496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10530496c8aeSRafael J. Wysocki 		return -EINVAL;
10540496c8aeSRafael J. Wysocki 
10550496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
1056596ba34bSRafael J. Wysocki }
1057596ba34bSRafael J. Wysocki 
1058596ba34bSRafael J. Wysocki /**
1059596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1060596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1061596ba34bSRafael J. Wysocki  *
1062596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1063596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1064596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1065596ba34bSRafael J. Wysocki  */
1066596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1067596ba34bSRafael J. Wysocki {
1068596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1069596ba34bSRafael J. Wysocki 
1070596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1071596ba34bSRafael J. Wysocki 
1072596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1073596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1074596ba34bSRafael J. Wysocki 		return -EINVAL;
1075596ba34bSRafael J. Wysocki 
1076d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1077596ba34bSRafael J. Wysocki }
1078596ba34bSRafael J. Wysocki 
1079596ba34bSRafael J. Wysocki /**
10800496c8aeSRafael J. Wysocki  * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1081596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1082596ba34bSRafael J. Wysocki  *
10830496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
10840496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1085596ba34bSRafael J. Wysocki  */
1086596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1087596ba34bSRafael J. Wysocki {
1088596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1089596ba34bSRafael J. Wysocki 
1090596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1091596ba34bSRafael J. Wysocki 
1092596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1093596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1094596ba34bSRafael J. Wysocki 		return -EINVAL;
1095596ba34bSRafael J. Wysocki 
1096596ba34bSRafael J. Wysocki 	/*
1097596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1098596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1099596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1100596ba34bSRafael J. Wysocki 	 */
110117b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
1102596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
1103596ba34bSRafael J. Wysocki 		/*
1104596ba34bSRafael J. Wysocki 		 * The boot kernel might put the domain into the power on state,
1105596ba34bSRafael J. Wysocki 		 * so make sure it really is powered off.
1106596ba34bSRafael J. Wysocki 		 */
1107596ba34bSRafael J. Wysocki 		if (genpd->power_off)
1108596ba34bSRafael J. Wysocki 			genpd->power_off(genpd);
1109596ba34bSRafael J. Wysocki 		return 0;
1110596ba34bSRafael J. Wysocki 	}
1111596ba34bSRafael J. Wysocki 
1112596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
1113596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
1114596ba34bSRafael J. Wysocki 
11150496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1116596ba34bSRafael J. Wysocki }
1117596ba34bSRafael J. Wysocki 
1118596ba34bSRafael J. Wysocki /**
1119596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1120596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1121596ba34bSRafael J. Wysocki  *
1122596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1123596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1124596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1125596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1126596ba34bSRafael J. Wysocki  */
1127596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1128596ba34bSRafael J. Wysocki {
1129596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1130596ba34bSRafael J. Wysocki 	bool run_complete;
1131596ba34bSRafael J. Wysocki 
1132596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1133596ba34bSRafael J. Wysocki 
1134596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1135596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1136596ba34bSRafael J. Wysocki 		return;
1137596ba34bSRafael J. Wysocki 
1138596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1139596ba34bSRafael J. Wysocki 
1140596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1141596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1142596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1143596ba34bSRafael J. Wysocki 
1144596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1145596ba34bSRafael J. Wysocki 
1146596ba34bSRafael J. Wysocki 	if (run_complete) {
1147596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
11486f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1149596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
11506f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1151596ba34bSRafael J. Wysocki 	}
1152596ba34bSRafael J. Wysocki }
1153596ba34bSRafael J. Wysocki 
1154596ba34bSRafael J. Wysocki #else
1155596ba34bSRafael J. Wysocki 
1156596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1157596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
11580496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late		NULL
1159596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
11600496c8aeSRafael J. Wysocki #define pm_genpd_resume_early		NULL
1161596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1162596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1163596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
11640496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late		NULL
1165596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
11660496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early		NULL
1167596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1168596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1169596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1170596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1171596ba34bSRafael J. Wysocki 
1172596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1173596ba34bSRafael J. Wysocki 
1174f721889fSRafael J. Wysocki /**
1175b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1176f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1177f721889fSRafael J. Wysocki  * @dev: Device to be added.
1178b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1179f721889fSRafael J. Wysocki  */
1180b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1181b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1182f721889fSRafael J. Wysocki {
1183cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
11844605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1185f721889fSRafael J. Wysocki 	int ret = 0;
1186f721889fSRafael J. Wysocki 
1187f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1188f721889fSRafael J. Wysocki 
1189f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1190f721889fSRafael J. Wysocki 		return -EINVAL;
1191f721889fSRafael J. Wysocki 
119217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1193f721889fSRafael J. Wysocki 
119417b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF) {
1195f721889fSRafael J. Wysocki 		ret = -EINVAL;
1196f721889fSRafael J. Wysocki 		goto out;
1197f721889fSRafael J. Wysocki 	}
1198f721889fSRafael J. Wysocki 
1199596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1200596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1201596ba34bSRafael J. Wysocki 		goto out;
1202596ba34bSRafael J. Wysocki 	}
1203596ba34bSRafael J. Wysocki 
12044605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
12054605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1206f721889fSRafael J. Wysocki 			ret = -EINVAL;
1207f721889fSRafael J. Wysocki 			goto out;
1208f721889fSRafael J. Wysocki 		}
1209f721889fSRafael J. Wysocki 
1210cd0ea672SRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
1211cd0ea672SRafael J. Wysocki 	if (!gpd_data) {
1212cd0ea672SRafael J. Wysocki 		ret = -ENOMEM;
1213cd0ea672SRafael J. Wysocki 		goto out;
1214cd0ea672SRafael J. Wysocki 	}
1215cd0ea672SRafael J. Wysocki 
1216596ba34bSRafael J. Wysocki 	genpd->device_count++;
1217f721889fSRafael J. Wysocki 
1218f721889fSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
12194605ab65SRafael J. Wysocki 	dev_pm_get_subsys_data(dev);
1220cd0ea672SRafael J. Wysocki 	dev->power.subsys_data->domain_data = &gpd_data->base;
1221cd0ea672SRafael J. Wysocki 	gpd_data->base.dev = dev;
1222cd0ea672SRafael J. Wysocki 	gpd_data->need_restore = false;
1223cd0ea672SRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1224b02c999aSRafael J. Wysocki 	if (td)
1225b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1226f721889fSRafael J. Wysocki 
1227f721889fSRafael J. Wysocki  out:
122817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1229f721889fSRafael J. Wysocki 
1230f721889fSRafael J. Wysocki 	return ret;
1231f721889fSRafael J. Wysocki }
1232f721889fSRafael J. Wysocki 
1233f721889fSRafael J. Wysocki /**
1234c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1235c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1236c8aa130bSThomas Abraham  *   the device is added to.
1237c8aa130bSThomas Abraham  * @dev: Device to be added.
1238c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1239c8aa130bSThomas Abraham  */
1240c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1241c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1242c8aa130bSThomas Abraham {
1243c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1244c8aa130bSThomas Abraham 
1245c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1246c8aa130bSThomas Abraham 
1247c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1248c8aa130bSThomas Abraham 		return -EINVAL;
1249c8aa130bSThomas Abraham 
1250c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1251c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1252c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1253c8aa130bSThomas Abraham 			genpd = gpd;
1254c8aa130bSThomas Abraham 			break;
1255c8aa130bSThomas Abraham 		}
1256c8aa130bSThomas Abraham 	}
1257c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1258c8aa130bSThomas Abraham 
1259c8aa130bSThomas Abraham 	if (!genpd)
1260c8aa130bSThomas Abraham 		return -EINVAL;
1261c8aa130bSThomas Abraham 
1262c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1263c8aa130bSThomas Abraham }
1264c8aa130bSThomas Abraham 
1265c8aa130bSThomas Abraham /**
1266f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1267f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1268f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1269f721889fSRafael J. Wysocki  */
1270f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1271f721889fSRafael J. Wysocki 			   struct device *dev)
1272f721889fSRafael J. Wysocki {
12734605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1274f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1275f721889fSRafael J. Wysocki 
1276f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1277f721889fSRafael J. Wysocki 
1278f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1279f721889fSRafael J. Wysocki 		return -EINVAL;
1280f721889fSRafael J. Wysocki 
128117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1282f721889fSRafael J. Wysocki 
1283596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1284596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1285596ba34bSRafael J. Wysocki 		goto out;
1286596ba34bSRafael J. Wysocki 	}
1287596ba34bSRafael J. Wysocki 
12884605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
12894605ab65SRafael J. Wysocki 		if (pdd->dev != dev)
1290f721889fSRafael J. Wysocki 			continue;
1291f721889fSRafael J. Wysocki 
12924605ab65SRafael J. Wysocki 		list_del_init(&pdd->list_node);
12934605ab65SRafael J. Wysocki 		pdd->dev = NULL;
12944605ab65SRafael J. Wysocki 		dev_pm_put_subsys_data(dev);
1295f721889fSRafael J. Wysocki 		dev->pm_domain = NULL;
1296cd0ea672SRafael J. Wysocki 		kfree(to_gpd_data(pdd));
1297f721889fSRafael J. Wysocki 
1298596ba34bSRafael J. Wysocki 		genpd->device_count--;
1299f721889fSRafael J. Wysocki 
1300f721889fSRafael J. Wysocki 		ret = 0;
1301f721889fSRafael J. Wysocki 		break;
1302f721889fSRafael J. Wysocki 	}
1303f721889fSRafael J. Wysocki 
1304596ba34bSRafael J. Wysocki  out:
130517b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1306f721889fSRafael J. Wysocki 
1307f721889fSRafael J. Wysocki 	return ret;
1308f721889fSRafael J. Wysocki }
1309f721889fSRafael J. Wysocki 
1310f721889fSRafael J. Wysocki /**
1311f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1312f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1313bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1314f721889fSRafael J. Wysocki  */
1315f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1316bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1317f721889fSRafael J. Wysocki {
13185063ce15SRafael J. Wysocki 	struct gpd_link *link;
1319f721889fSRafael J. Wysocki 	int ret = 0;
1320f721889fSRafael J. Wysocki 
1321bc0403ffSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1322f721889fSRafael J. Wysocki 		return -EINVAL;
1323f721889fSRafael J. Wysocki 
132417b75ecaSRafael J. Wysocki  start:
132517b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1326bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1327f721889fSRafael J. Wysocki 
1328bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1329bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1330bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
133117b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
133217b75ecaSRafael J. Wysocki 		goto start;
133317b75ecaSRafael J. Wysocki 	}
133417b75ecaSRafael J. Wysocki 
133517b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1336bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1337f721889fSRafael J. Wysocki 		ret = -EINVAL;
1338f721889fSRafael J. Wysocki 		goto out;
1339f721889fSRafael J. Wysocki 	}
1340f721889fSRafael J. Wysocki 
13415063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1342bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1343f721889fSRafael J. Wysocki 			ret = -EINVAL;
1344f721889fSRafael J. Wysocki 			goto out;
1345f721889fSRafael J. Wysocki 		}
1346f721889fSRafael J. Wysocki 	}
1347f721889fSRafael J. Wysocki 
13485063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
13495063ce15SRafael J. Wysocki 	if (!link) {
13505063ce15SRafael J. Wysocki 		ret = -ENOMEM;
13515063ce15SRafael J. Wysocki 		goto out;
13525063ce15SRafael J. Wysocki 	}
13535063ce15SRafael J. Wysocki 	link->master = genpd;
13545063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1355bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1356bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1357bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1358c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1359f721889fSRafael J. Wysocki 
1360f721889fSRafael J. Wysocki  out:
1361bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
136217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1363f721889fSRafael J. Wysocki 
1364f721889fSRafael J. Wysocki 	return ret;
1365f721889fSRafael J. Wysocki }
1366f721889fSRafael J. Wysocki 
1367f721889fSRafael J. Wysocki /**
1368f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1369f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
13705063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1371f721889fSRafael J. Wysocki  */
1372f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
13735063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1374f721889fSRafael J. Wysocki {
13755063ce15SRafael J. Wysocki 	struct gpd_link *link;
1376f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1377f721889fSRafael J. Wysocki 
13785063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1379f721889fSRafael J. Wysocki 		return -EINVAL;
1380f721889fSRafael J. Wysocki 
138117b75ecaSRafael J. Wysocki  start:
138217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1383f721889fSRafael J. Wysocki 
13845063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
13855063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1386f721889fSRafael J. Wysocki 			continue;
1387f721889fSRafael J. Wysocki 
1388f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1389f721889fSRafael J. Wysocki 
139017b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
139117b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
139217b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
139317b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
139417b75ecaSRafael J. Wysocki 			goto start;
139517b75ecaSRafael J. Wysocki 		}
139617b75ecaSRafael J. Wysocki 
13975063ce15SRafael J. Wysocki 		list_del(&link->master_node);
13985063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
13995063ce15SRafael J. Wysocki 		kfree(link);
140017b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1401f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1402f721889fSRafael J. Wysocki 
1403f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1404f721889fSRafael J. Wysocki 
1405f721889fSRafael J. Wysocki 		ret = 0;
1406f721889fSRafael J. Wysocki 		break;
1407f721889fSRafael J. Wysocki 	}
1408f721889fSRafael J. Wysocki 
140917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1410f721889fSRafael J. Wysocki 
1411f721889fSRafael J. Wysocki 	return ret;
1412f721889fSRafael J. Wysocki }
1413f721889fSRafael J. Wysocki 
1414f721889fSRafael J. Wysocki /**
1415d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1416d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1417d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1418b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
1419d5e4cbfeSRafael J. Wysocki  */
1420b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1421b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1422d5e4cbfeSRafael J. Wysocki {
1423d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1424d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1425d5e4cbfeSRafael J. Wysocki 
1426d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data && ops))
1427d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1428d5e4cbfeSRafael J. Wysocki 
1429d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1430d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1431d5e4cbfeSRafael J. Wysocki 
1432d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1433d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1434d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1435d5e4cbfeSRafael J. Wysocki 
1436d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = *ops;
1437b02c999aSRafael J. Wysocki 		if (td)
1438b02c999aSRafael J. Wysocki 			gpd_data->td = *td;
1439d5e4cbfeSRafael J. Wysocki 	} else {
1440d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1441d5e4cbfeSRafael J. Wysocki 	}
1442d5e4cbfeSRafael J. Wysocki 
1443d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1444d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1445d5e4cbfeSRafael J. Wysocki 
1446d5e4cbfeSRafael J. Wysocki 	return ret;
1447d5e4cbfeSRafael J. Wysocki }
1448d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1449d5e4cbfeSRafael J. Wysocki 
1450d5e4cbfeSRafael J. Wysocki /**
1451b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1452d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1453b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
1454d5e4cbfeSRafael J. Wysocki  */
1455b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1456d5e4cbfeSRafael J. Wysocki {
1457d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1458d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1459d5e4cbfeSRafael J. Wysocki 
1460d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1461d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1462d5e4cbfeSRafael J. Wysocki 
1463d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1464d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1465d5e4cbfeSRafael J. Wysocki 
1466d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1467d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1468d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1469d5e4cbfeSRafael J. Wysocki 
1470d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = (struct gpd_dev_ops){ 0 };
1471b02c999aSRafael J. Wysocki 		if (clear_td)
1472b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
1473d5e4cbfeSRafael J. Wysocki 	} else {
1474d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1475d5e4cbfeSRafael J. Wysocki 	}
1476d5e4cbfeSRafael J. Wysocki 
1477d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1478d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1479d5e4cbfeSRafael J. Wysocki 
1480d5e4cbfeSRafael J. Wysocki 	return ret;
1481d5e4cbfeSRafael J. Wysocki }
1482b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1483d5e4cbfeSRafael J. Wysocki 
1484d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1485d23b9b00SRafael J. Wysocki 
1486d5e4cbfeSRafael J. Wysocki /**
1487ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1488ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1489ecf00475SRafael J. Wysocki  */
1490ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1491ecf00475SRafael J. Wysocki {
1492ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1493ecf00475SRafael J. Wysocki 	struct device_driver *drv = dev->driver;
1494ecf00475SRafael J. Wysocki 
1495ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1496ecf00475SRafael J. Wysocki 	if (cb)
1497ecf00475SRafael J. Wysocki 		return cb(dev);
1498ecf00475SRafael J. Wysocki 
1499ecf00475SRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_suspend)
1500ecf00475SRafael J. Wysocki 		return drv->pm->runtime_suspend(dev);
1501ecf00475SRafael J. Wysocki 
1502ecf00475SRafael J. Wysocki 	return 0;
1503ecf00475SRafael J. Wysocki }
1504ecf00475SRafael J. Wysocki 
1505ecf00475SRafael J. Wysocki /**
1506ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1507ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1508ecf00475SRafael J. Wysocki  */
1509ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1510ecf00475SRafael J. Wysocki {
1511ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1512ecf00475SRafael J. Wysocki 	struct device_driver *drv = dev->driver;
1513ecf00475SRafael J. Wysocki 
1514ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1515ecf00475SRafael J. Wysocki 	if (cb)
1516ecf00475SRafael J. Wysocki 		return cb(dev);
1517ecf00475SRafael J. Wysocki 
1518ecf00475SRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_resume)
1519ecf00475SRafael J. Wysocki 		return drv->pm->runtime_resume(dev);
1520ecf00475SRafael J. Wysocki 
1521ecf00475SRafael J. Wysocki 	return 0;
1522ecf00475SRafael J. Wysocki }
1523ecf00475SRafael J. Wysocki 
15240f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
15250f1d6986SRafael J. Wysocki 
1526ecf00475SRafael J. Wysocki /**
1527d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
1528d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1529d23b9b00SRafael J. Wysocki  */
1530d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
1531d23b9b00SRafael J. Wysocki {
1532c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
1533d23b9b00SRafael J. Wysocki 
1534d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
1535d23b9b00SRafael J. Wysocki }
1536d23b9b00SRafael J. Wysocki 
1537d23b9b00SRafael J. Wysocki /**
1538d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
1539d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1540d23b9b00SRafael J. Wysocki  */
1541d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
1542d23b9b00SRafael J. Wysocki {
1543c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
1544d23b9b00SRafael J. Wysocki 
15450496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_late(dev);
1546d23b9b00SRafael J. Wysocki }
1547d23b9b00SRafael J. Wysocki 
1548d23b9b00SRafael J. Wysocki /**
1549d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
1550d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1551d23b9b00SRafael J. Wysocki  */
1552d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
1553d23b9b00SRafael J. Wysocki {
1554c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
1555d23b9b00SRafael J. Wysocki 
15560496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_early(dev);
1557d23b9b00SRafael J. Wysocki }
1558d23b9b00SRafael J. Wysocki 
1559d23b9b00SRafael J. Wysocki /**
1560d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
1561d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1562d23b9b00SRafael J. Wysocki  */
1563d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
1564d23b9b00SRafael J. Wysocki {
1565c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
1566d23b9b00SRafael J. Wysocki 
1567d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
1568d23b9b00SRafael J. Wysocki }
1569d23b9b00SRafael J. Wysocki 
1570d23b9b00SRafael J. Wysocki /**
1571d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
1572d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1573d23b9b00SRafael J. Wysocki  */
1574d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
1575d23b9b00SRafael J. Wysocki {
1576d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
1577d23b9b00SRafael J. Wysocki 
1578d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
1579d23b9b00SRafael J. Wysocki }
1580d23b9b00SRafael J. Wysocki 
1581d23b9b00SRafael J. Wysocki /**
1582d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
1583d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1584d23b9b00SRafael J. Wysocki  */
1585d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
1586d23b9b00SRafael J. Wysocki {
1587d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
1588d23b9b00SRafael J. Wysocki 
15890496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_late(dev);
1590d23b9b00SRafael J. Wysocki }
1591d23b9b00SRafael J. Wysocki 
1592d23b9b00SRafael J. Wysocki /**
1593d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
1594d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1595d23b9b00SRafael J. Wysocki  */
1596d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
1597d23b9b00SRafael J. Wysocki {
1598d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
1599d23b9b00SRafael J. Wysocki 
16000496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_early(dev);
1601d23b9b00SRafael J. Wysocki }
1602d23b9b00SRafael J. Wysocki 
1603d23b9b00SRafael J. Wysocki /**
1604d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
1605d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1606d23b9b00SRafael J. Wysocki  */
1607d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
1608d23b9b00SRafael J. Wysocki {
1609d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
1610d23b9b00SRafael J. Wysocki 
1611d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
1612d23b9b00SRafael J. Wysocki }
1613d23b9b00SRafael J. Wysocki 
16140f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
16150f1d6986SRafael J. Wysocki 
16160f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
16170f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
16180f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
16190f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
16200f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
16210f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
16220f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
16230f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
16240f1d6986SRafael J. Wysocki 
16250f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
16260f1d6986SRafael J. Wysocki 
1627d23b9b00SRafael J. Wysocki /**
1628f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1629f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1630f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1631f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1632f721889fSRafael J. Wysocki  */
1633f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1634f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1635f721889fSRafael J. Wysocki {
1636f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1637f721889fSRafael J. Wysocki 		return;
1638f721889fSRafael J. Wysocki 
16395063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
16405063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
1641f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1642f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1643f721889fSRafael J. Wysocki 	genpd->gov = gov;
1644f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1645f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1646c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
164717b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
164817b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1649c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1650c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1651596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1652596ba34bSRafael J. Wysocki 	genpd->suspended_count = 0;
1653221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
1654f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1655f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1656f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1657596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1658596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
16590496c8aeSRafael J. Wysocki 	genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
1660596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1661596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
16620496c8aeSRafael J. Wysocki 	genpd->domain.ops.resume_early = pm_genpd_resume_early;
1663596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1664596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
16650496c8aeSRafael J. Wysocki 	genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
1666596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1667596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
16680496c8aeSRafael J. Wysocki 	genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
1669596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1670d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
16710496c8aeSRafael J. Wysocki 	genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
1672d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
1673596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
16740496c8aeSRafael J. Wysocki 	genpd->domain.ops.restore_early = pm_genpd_resume_early;
1675d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
1676596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
1677ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
1678ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
1679c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
1680c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
1681c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
1682c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
1683d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
1684d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
1685d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
1686d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
16875125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
16885125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
16895125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
16905125bbf3SRafael J. Wysocki }
1691