xref: /openbmc/linux/drivers/base/power/domain.c (revision c8aa130b)
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 /**
823596ba34bSRafael J. Wysocki  * pm_genpd_suspend_noirq - 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  */
830596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
831596ba34bSRafael J. Wysocki {
832596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
833596ba34bSRafael J. Wysocki 	int ret;
834596ba34bSRafael J. Wysocki 
835596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
836596ba34bSRafael J. Wysocki 
837596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
838596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
839596ba34bSRafael J. Wysocki 		return -EINVAL;
840596ba34bSRafael J. Wysocki 
841596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
842596ba34bSRafael J. Wysocki 		return 0;
843596ba34bSRafael J. Wysocki 
844d23b9b00SRafael J. Wysocki 	ret = genpd_suspend_late(genpd, dev);
845596ba34bSRafael J. Wysocki 	if (ret)
846596ba34bSRafael J. Wysocki 		return ret;
847596ba34bSRafael J. Wysocki 
848d5e4cbfeSRafael J. Wysocki 	if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
849d4f2d87aSRafael J. Wysocki 		return 0;
850d4f2d87aSRafael J. Wysocki 
851d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
852596ba34bSRafael J. Wysocki 
853596ba34bSRafael J. Wysocki 	/*
854596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
855596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
856596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
857596ba34bSRafael J. Wysocki 	 */
858596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
859596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
860596ba34bSRafael J. Wysocki 
861596ba34bSRafael J. Wysocki 	return 0;
862596ba34bSRafael J. Wysocki }
863596ba34bSRafael J. Wysocki 
864596ba34bSRafael J. Wysocki /**
865596ba34bSRafael J. Wysocki  * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
866596ba34bSRafael J. Wysocki  * @dev: Device to resume.
867596ba34bSRafael J. Wysocki  *
868596ba34bSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
869596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
870596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
871596ba34bSRafael J. Wysocki  * devices.
872596ba34bSRafael J. Wysocki  */
873596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
874596ba34bSRafael J. Wysocki {
875596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
876596ba34bSRafael J. Wysocki 
877596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
878596ba34bSRafael J. Wysocki 
879596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
880596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
881596ba34bSRafael J. Wysocki 		return -EINVAL;
882596ba34bSRafael J. Wysocki 
883596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
884596ba34bSRafael J. Wysocki 		return 0;
885596ba34bSRafael J. Wysocki 
886596ba34bSRafael J. Wysocki 	/*
887596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
888596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
889596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
890596ba34bSRafael J. Wysocki 	 */
891596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
892596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
893d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
894596ba34bSRafael J. Wysocki 
895d23b9b00SRafael J. Wysocki 	return genpd_resume_early(genpd, dev);
896596ba34bSRafael J. Wysocki }
897596ba34bSRafael J. Wysocki 
898596ba34bSRafael J. Wysocki /**
899596ba34bSRafael J. Wysocki  * pm_genpd_resume - Resume a device belonging to an I/O power domain.
900596ba34bSRafael J. Wysocki  * @dev: Device to resume.
901596ba34bSRafael J. Wysocki  *
902596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
903596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
904596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
905596ba34bSRafael J. Wysocki  */
906596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
907596ba34bSRafael J. Wysocki {
908596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
909596ba34bSRafael J. Wysocki 
910596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
911596ba34bSRafael J. Wysocki 
912596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
913596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
914596ba34bSRafael J. Wysocki 		return -EINVAL;
915596ba34bSRafael J. Wysocki 
916d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
917596ba34bSRafael J. Wysocki }
918596ba34bSRafael J. Wysocki 
919596ba34bSRafael J. Wysocki /**
920596ba34bSRafael J. Wysocki  * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
921596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
922596ba34bSRafael J. Wysocki  *
923596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
924596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
925596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
926596ba34bSRafael J. Wysocki  */
927596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
928596ba34bSRafael J. Wysocki {
929596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
930596ba34bSRafael J. Wysocki 
931596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
932596ba34bSRafael J. Wysocki 
933596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
934596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
935596ba34bSRafael J. Wysocki 		return -EINVAL;
936596ba34bSRafael J. Wysocki 
937d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
938596ba34bSRafael J. Wysocki }
939596ba34bSRafael J. Wysocki 
940596ba34bSRafael J. Wysocki /**
941596ba34bSRafael J. Wysocki  * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
942596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
943596ba34bSRafael J. Wysocki  *
944596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
945596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
946596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
947596ba34bSRafael J. Wysocki  * devices.
948596ba34bSRafael J. Wysocki  */
949596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
950596ba34bSRafael J. Wysocki {
951596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
952596ba34bSRafael J. Wysocki 	int ret;
953596ba34bSRafael J. Wysocki 
954596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
955596ba34bSRafael J. Wysocki 
956596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
957596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
958596ba34bSRafael J. Wysocki 		return -EINVAL;
959596ba34bSRafael J. Wysocki 
960596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
961596ba34bSRafael J. Wysocki 		return 0;
962596ba34bSRafael J. Wysocki 
963d23b9b00SRafael J. Wysocki 	ret = genpd_freeze_late(genpd, dev);
964596ba34bSRafael J. Wysocki 	if (ret)
965596ba34bSRafael J. Wysocki 		return ret;
966596ba34bSRafael J. Wysocki 
967d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
968596ba34bSRafael J. Wysocki 
969596ba34bSRafael J. Wysocki 	return 0;
970596ba34bSRafael J. Wysocki }
971596ba34bSRafael J. Wysocki 
972596ba34bSRafael J. Wysocki /**
973596ba34bSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
974596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
975596ba34bSRafael J. Wysocki  *
976596ba34bSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
977596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
978596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
979596ba34bSRafael J. Wysocki  * devices.
980596ba34bSRafael J. Wysocki  */
981596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
982596ba34bSRafael J. Wysocki {
983596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
984596ba34bSRafael J. Wysocki 
985596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
986596ba34bSRafael J. Wysocki 
987596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
988596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
989596ba34bSRafael J. Wysocki 		return -EINVAL;
990596ba34bSRafael J. Wysocki 
991596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
992596ba34bSRafael J. Wysocki 		return 0;
993596ba34bSRafael J. Wysocki 
994d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
995596ba34bSRafael J. Wysocki 
996d23b9b00SRafael J. Wysocki 	return genpd_thaw_early(genpd, dev);
997596ba34bSRafael J. Wysocki }
998596ba34bSRafael J. Wysocki 
999596ba34bSRafael J. Wysocki /**
1000596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1001596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1002596ba34bSRafael J. Wysocki  *
1003596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1004596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1005596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1006596ba34bSRafael J. Wysocki  */
1007596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1008596ba34bSRafael J. Wysocki {
1009596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1010596ba34bSRafael J. Wysocki 
1011596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1012596ba34bSRafael J. Wysocki 
1013596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1014596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1015596ba34bSRafael J. Wysocki 		return -EINVAL;
1016596ba34bSRafael J. Wysocki 
1017d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1018596ba34bSRafael J. Wysocki }
1019596ba34bSRafael J. Wysocki 
1020596ba34bSRafael J. Wysocki /**
1021596ba34bSRafael J. Wysocki  * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
1022596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1023596ba34bSRafael J. Wysocki  *
1024596ba34bSRafael J. Wysocki  * Carry out an early restore of a device under the assumption that its
1025596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1026596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1027596ba34bSRafael J. Wysocki  * devices.
1028596ba34bSRafael J. Wysocki  */
1029596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1030596ba34bSRafael J. Wysocki {
1031596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1032596ba34bSRafael J. Wysocki 
1033596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1034596ba34bSRafael J. Wysocki 
1035596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1036596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1037596ba34bSRafael J. Wysocki 		return -EINVAL;
1038596ba34bSRafael J. Wysocki 
1039596ba34bSRafael J. Wysocki 	/*
1040596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1041596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1042596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1043596ba34bSRafael J. Wysocki 	 */
104417b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
1045596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
1046596ba34bSRafael J. Wysocki 		/*
1047596ba34bSRafael J. Wysocki 		 * The boot kernel might put the domain into the power on state,
1048596ba34bSRafael J. Wysocki 		 * so make sure it really is powered off.
1049596ba34bSRafael J. Wysocki 		 */
1050596ba34bSRafael J. Wysocki 		if (genpd->power_off)
1051596ba34bSRafael J. Wysocki 			genpd->power_off(genpd);
1052596ba34bSRafael J. Wysocki 		return 0;
1053596ba34bSRafael J. Wysocki 	}
1054596ba34bSRafael J. Wysocki 
1055596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
1056596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
1057d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
1058596ba34bSRafael J. Wysocki 
1059d23b9b00SRafael J. Wysocki 	return genpd_resume_early(genpd, dev);
1060596ba34bSRafael J. Wysocki }
1061596ba34bSRafael J. Wysocki 
1062596ba34bSRafael J. Wysocki /**
1063596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1064596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1065596ba34bSRafael J. Wysocki  *
1066596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1067596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1068596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1069596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1070596ba34bSRafael J. Wysocki  */
1071596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1072596ba34bSRafael J. Wysocki {
1073596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1074596ba34bSRafael J. Wysocki 	bool run_complete;
1075596ba34bSRafael J. Wysocki 
1076596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1077596ba34bSRafael J. Wysocki 
1078596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1079596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1080596ba34bSRafael J. Wysocki 		return;
1081596ba34bSRafael J. Wysocki 
1082596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1083596ba34bSRafael J. Wysocki 
1084596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1085596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1086596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1087596ba34bSRafael J. Wysocki 
1088596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1089596ba34bSRafael J. Wysocki 
1090596ba34bSRafael J. Wysocki 	if (run_complete) {
1091596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
10926f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1093596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
10946f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1095596ba34bSRafael J. Wysocki 	}
1096596ba34bSRafael J. Wysocki }
1097596ba34bSRafael J. Wysocki 
1098596ba34bSRafael J. Wysocki #else
1099596ba34bSRafael J. Wysocki 
1100596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1101596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
1102596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
1103596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1104596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1105596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
1106596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
1107596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1108596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1109596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1110596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1111596ba34bSRafael J. Wysocki 
1112596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1113596ba34bSRafael J. Wysocki 
1114f721889fSRafael J. Wysocki /**
1115b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1116f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1117f721889fSRafael J. Wysocki  * @dev: Device to be added.
1118b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1119f721889fSRafael J. Wysocki  */
1120b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1121b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1122f721889fSRafael J. Wysocki {
1123cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
11244605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1125f721889fSRafael J. Wysocki 	int ret = 0;
1126f721889fSRafael J. Wysocki 
1127f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1128f721889fSRafael J. Wysocki 
1129f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1130f721889fSRafael J. Wysocki 		return -EINVAL;
1131f721889fSRafael J. Wysocki 
113217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1133f721889fSRafael J. Wysocki 
113417b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF) {
1135f721889fSRafael J. Wysocki 		ret = -EINVAL;
1136f721889fSRafael J. Wysocki 		goto out;
1137f721889fSRafael J. Wysocki 	}
1138f721889fSRafael J. Wysocki 
1139596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1140596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1141596ba34bSRafael J. Wysocki 		goto out;
1142596ba34bSRafael J. Wysocki 	}
1143596ba34bSRafael J. Wysocki 
11444605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
11454605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1146f721889fSRafael J. Wysocki 			ret = -EINVAL;
1147f721889fSRafael J. Wysocki 			goto out;
1148f721889fSRafael J. Wysocki 		}
1149f721889fSRafael J. Wysocki 
1150cd0ea672SRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
1151cd0ea672SRafael J. Wysocki 	if (!gpd_data) {
1152cd0ea672SRafael J. Wysocki 		ret = -ENOMEM;
1153cd0ea672SRafael J. Wysocki 		goto out;
1154cd0ea672SRafael J. Wysocki 	}
1155cd0ea672SRafael J. Wysocki 
1156596ba34bSRafael J. Wysocki 	genpd->device_count++;
1157f721889fSRafael J. Wysocki 
1158f721889fSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
11594605ab65SRafael J. Wysocki 	dev_pm_get_subsys_data(dev);
1160cd0ea672SRafael J. Wysocki 	dev->power.subsys_data->domain_data = &gpd_data->base;
1161cd0ea672SRafael J. Wysocki 	gpd_data->base.dev = dev;
1162cd0ea672SRafael J. Wysocki 	gpd_data->need_restore = false;
1163cd0ea672SRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1164b02c999aSRafael J. Wysocki 	if (td)
1165b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1166f721889fSRafael J. Wysocki 
1167f721889fSRafael J. Wysocki  out:
116817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1169f721889fSRafael J. Wysocki 
1170f721889fSRafael J. Wysocki 	return ret;
1171f721889fSRafael J. Wysocki }
1172f721889fSRafael J. Wysocki 
1173f721889fSRafael J. Wysocki /**
1174c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1175c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1176c8aa130bSThomas Abraham  *   the device is added to.
1177c8aa130bSThomas Abraham  * @dev: Device to be added.
1178c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1179c8aa130bSThomas Abraham  */
1180c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1181c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1182c8aa130bSThomas Abraham {
1183c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1184c8aa130bSThomas Abraham 
1185c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1186c8aa130bSThomas Abraham 
1187c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1188c8aa130bSThomas Abraham 		return -EINVAL;
1189c8aa130bSThomas Abraham 
1190c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1191c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1192c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1193c8aa130bSThomas Abraham 			genpd = gpd;
1194c8aa130bSThomas Abraham 			break;
1195c8aa130bSThomas Abraham 		}
1196c8aa130bSThomas Abraham 	}
1197c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1198c8aa130bSThomas Abraham 
1199c8aa130bSThomas Abraham 	if (!genpd)
1200c8aa130bSThomas Abraham 		return -EINVAL;
1201c8aa130bSThomas Abraham 
1202c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1203c8aa130bSThomas Abraham }
1204c8aa130bSThomas Abraham 
1205c8aa130bSThomas Abraham /**
1206f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1207f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1208f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1209f721889fSRafael J. Wysocki  */
1210f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1211f721889fSRafael J. Wysocki 			   struct device *dev)
1212f721889fSRafael J. Wysocki {
12134605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1214f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1215f721889fSRafael J. Wysocki 
1216f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1217f721889fSRafael J. Wysocki 
1218f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1219f721889fSRafael J. Wysocki 		return -EINVAL;
1220f721889fSRafael J. Wysocki 
122117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1222f721889fSRafael J. Wysocki 
1223596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1224596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1225596ba34bSRafael J. Wysocki 		goto out;
1226596ba34bSRafael J. Wysocki 	}
1227596ba34bSRafael J. Wysocki 
12284605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
12294605ab65SRafael J. Wysocki 		if (pdd->dev != dev)
1230f721889fSRafael J. Wysocki 			continue;
1231f721889fSRafael J. Wysocki 
12324605ab65SRafael J. Wysocki 		list_del_init(&pdd->list_node);
12334605ab65SRafael J. Wysocki 		pdd->dev = NULL;
12344605ab65SRafael J. Wysocki 		dev_pm_put_subsys_data(dev);
1235f721889fSRafael J. Wysocki 		dev->pm_domain = NULL;
1236cd0ea672SRafael J. Wysocki 		kfree(to_gpd_data(pdd));
1237f721889fSRafael J. Wysocki 
1238596ba34bSRafael J. Wysocki 		genpd->device_count--;
1239f721889fSRafael J. Wysocki 
1240f721889fSRafael J. Wysocki 		ret = 0;
1241f721889fSRafael J. Wysocki 		break;
1242f721889fSRafael J. Wysocki 	}
1243f721889fSRafael J. Wysocki 
1244596ba34bSRafael J. Wysocki  out:
124517b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1246f721889fSRafael J. Wysocki 
1247f721889fSRafael J. Wysocki 	return ret;
1248f721889fSRafael J. Wysocki }
1249f721889fSRafael J. Wysocki 
1250f721889fSRafael J. Wysocki /**
1251f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1252f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1253bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1254f721889fSRafael J. Wysocki  */
1255f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1256bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1257f721889fSRafael J. Wysocki {
12585063ce15SRafael J. Wysocki 	struct gpd_link *link;
1259f721889fSRafael J. Wysocki 	int ret = 0;
1260f721889fSRafael J. Wysocki 
1261bc0403ffSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1262f721889fSRafael J. Wysocki 		return -EINVAL;
1263f721889fSRafael J. Wysocki 
126417b75ecaSRafael J. Wysocki  start:
126517b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1266bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1267f721889fSRafael J. Wysocki 
1268bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1269bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1270bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
127117b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
127217b75ecaSRafael J. Wysocki 		goto start;
127317b75ecaSRafael J. Wysocki 	}
127417b75ecaSRafael J. Wysocki 
127517b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1276bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1277f721889fSRafael J. Wysocki 		ret = -EINVAL;
1278f721889fSRafael J. Wysocki 		goto out;
1279f721889fSRafael J. Wysocki 	}
1280f721889fSRafael J. Wysocki 
12815063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1282bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1283f721889fSRafael J. Wysocki 			ret = -EINVAL;
1284f721889fSRafael J. Wysocki 			goto out;
1285f721889fSRafael J. Wysocki 		}
1286f721889fSRafael J. Wysocki 	}
1287f721889fSRafael J. Wysocki 
12885063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
12895063ce15SRafael J. Wysocki 	if (!link) {
12905063ce15SRafael J. Wysocki 		ret = -ENOMEM;
12915063ce15SRafael J. Wysocki 		goto out;
12925063ce15SRafael J. Wysocki 	}
12935063ce15SRafael J. Wysocki 	link->master = genpd;
12945063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1295bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1296bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1297bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1298c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1299f721889fSRafael J. Wysocki 
1300f721889fSRafael J. Wysocki  out:
1301bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
130217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1303f721889fSRafael J. Wysocki 
1304f721889fSRafael J. Wysocki 	return ret;
1305f721889fSRafael J. Wysocki }
1306f721889fSRafael J. Wysocki 
1307f721889fSRafael J. Wysocki /**
1308f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1309f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
13105063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1311f721889fSRafael J. Wysocki  */
1312f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
13135063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1314f721889fSRafael J. Wysocki {
13155063ce15SRafael J. Wysocki 	struct gpd_link *link;
1316f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1317f721889fSRafael J. Wysocki 
13185063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1319f721889fSRafael J. Wysocki 		return -EINVAL;
1320f721889fSRafael J. Wysocki 
132117b75ecaSRafael J. Wysocki  start:
132217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1323f721889fSRafael J. Wysocki 
13245063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
13255063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1326f721889fSRafael J. Wysocki 			continue;
1327f721889fSRafael J. Wysocki 
1328f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1329f721889fSRafael J. Wysocki 
133017b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
133117b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
133217b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
133317b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
133417b75ecaSRafael J. Wysocki 			goto start;
133517b75ecaSRafael J. Wysocki 		}
133617b75ecaSRafael J. Wysocki 
13375063ce15SRafael J. Wysocki 		list_del(&link->master_node);
13385063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
13395063ce15SRafael J. Wysocki 		kfree(link);
134017b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1341f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1342f721889fSRafael J. Wysocki 
1343f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1344f721889fSRafael J. Wysocki 
1345f721889fSRafael J. Wysocki 		ret = 0;
1346f721889fSRafael J. Wysocki 		break;
1347f721889fSRafael J. Wysocki 	}
1348f721889fSRafael J. Wysocki 
134917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1350f721889fSRafael J. Wysocki 
1351f721889fSRafael J. Wysocki 	return ret;
1352f721889fSRafael J. Wysocki }
1353f721889fSRafael J. Wysocki 
1354f721889fSRafael J. Wysocki /**
1355d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1356d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1357d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1358b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
1359d5e4cbfeSRafael J. Wysocki  */
1360b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1361b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1362d5e4cbfeSRafael J. Wysocki {
1363d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1364d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1365d5e4cbfeSRafael J. Wysocki 
1366d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data && ops))
1367d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1368d5e4cbfeSRafael J. Wysocki 
1369d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1370d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1371d5e4cbfeSRafael J. Wysocki 
1372d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1373d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1374d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1375d5e4cbfeSRafael J. Wysocki 
1376d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = *ops;
1377b02c999aSRafael J. Wysocki 		if (td)
1378b02c999aSRafael J. Wysocki 			gpd_data->td = *td;
1379d5e4cbfeSRafael J. Wysocki 	} else {
1380d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1381d5e4cbfeSRafael J. Wysocki 	}
1382d5e4cbfeSRafael J. Wysocki 
1383d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1384d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1385d5e4cbfeSRafael J. Wysocki 
1386d5e4cbfeSRafael J. Wysocki 	return ret;
1387d5e4cbfeSRafael J. Wysocki }
1388d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1389d5e4cbfeSRafael J. Wysocki 
1390d5e4cbfeSRafael J. Wysocki /**
1391b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1392d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1393b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
1394d5e4cbfeSRafael J. Wysocki  */
1395b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1396d5e4cbfeSRafael J. Wysocki {
1397d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1398d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1399d5e4cbfeSRafael J. Wysocki 
1400d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1401d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1402d5e4cbfeSRafael J. Wysocki 
1403d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1404d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1405d5e4cbfeSRafael J. Wysocki 
1406d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1407d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1408d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1409d5e4cbfeSRafael J. Wysocki 
1410d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = (struct gpd_dev_ops){ 0 };
1411b02c999aSRafael J. Wysocki 		if (clear_td)
1412b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
1413d5e4cbfeSRafael J. Wysocki 	} else {
1414d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1415d5e4cbfeSRafael J. Wysocki 	}
1416d5e4cbfeSRafael J. Wysocki 
1417d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1418d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1419d5e4cbfeSRafael J. Wysocki 
1420d5e4cbfeSRafael J. Wysocki 	return ret;
1421d5e4cbfeSRafael J. Wysocki }
1422b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1423d5e4cbfeSRafael J. Wysocki 
1424d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1425d23b9b00SRafael J. Wysocki 
1426d5e4cbfeSRafael J. Wysocki /**
1427ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1428ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1429ecf00475SRafael J. Wysocki  */
1430ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1431ecf00475SRafael J. Wysocki {
1432ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1433ecf00475SRafael J. Wysocki 	struct device_driver *drv = dev->driver;
1434ecf00475SRafael J. Wysocki 
1435ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1436ecf00475SRafael J. Wysocki 	if (cb)
1437ecf00475SRafael J. Wysocki 		return cb(dev);
1438ecf00475SRafael J. Wysocki 
1439ecf00475SRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_suspend)
1440ecf00475SRafael J. Wysocki 		return drv->pm->runtime_suspend(dev);
1441ecf00475SRafael J. Wysocki 
1442ecf00475SRafael J. Wysocki 	return 0;
1443ecf00475SRafael J. Wysocki }
1444ecf00475SRafael J. Wysocki 
1445ecf00475SRafael J. Wysocki /**
1446ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1447ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1448ecf00475SRafael J. Wysocki  */
1449ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1450ecf00475SRafael J. Wysocki {
1451ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1452ecf00475SRafael J. Wysocki 	struct device_driver *drv = dev->driver;
1453ecf00475SRafael J. Wysocki 
1454ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1455ecf00475SRafael J. Wysocki 	if (cb)
1456ecf00475SRafael J. Wysocki 		return cb(dev);
1457ecf00475SRafael J. Wysocki 
1458ecf00475SRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_resume)
1459ecf00475SRafael J. Wysocki 		return drv->pm->runtime_resume(dev);
1460ecf00475SRafael J. Wysocki 
1461ecf00475SRafael J. Wysocki 	return 0;
1462ecf00475SRafael J. Wysocki }
1463ecf00475SRafael J. Wysocki 
14640f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
14650f1d6986SRafael J. Wysocki 
1466ecf00475SRafael J. Wysocki /**
1467d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
1468d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1469d23b9b00SRafael J. Wysocki  */
1470d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
1471d23b9b00SRafael J. Wysocki {
1472c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
1473d23b9b00SRafael J. Wysocki 
1474d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
1475d23b9b00SRafael J. Wysocki }
1476d23b9b00SRafael J. Wysocki 
1477d23b9b00SRafael J. Wysocki /**
1478d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
1479d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1480d23b9b00SRafael J. Wysocki  */
1481d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
1482d23b9b00SRafael J. Wysocki {
1483c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
1484d23b9b00SRafael J. Wysocki 
1485d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_noirq(dev);
1486d23b9b00SRafael J. Wysocki }
1487d23b9b00SRafael J. Wysocki 
1488d23b9b00SRafael J. Wysocki /**
1489d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
1490d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1491d23b9b00SRafael J. Wysocki  */
1492d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
1493d23b9b00SRafael J. Wysocki {
1494c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
1495d23b9b00SRafael J. Wysocki 
1496d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_noirq(dev);
1497d23b9b00SRafael J. Wysocki }
1498d23b9b00SRafael J. Wysocki 
1499d23b9b00SRafael J. Wysocki /**
1500d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
1501d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1502d23b9b00SRafael J. Wysocki  */
1503d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
1504d23b9b00SRafael J. Wysocki {
1505c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
1506d23b9b00SRafael J. Wysocki 
1507d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
1508d23b9b00SRafael J. Wysocki }
1509d23b9b00SRafael J. Wysocki 
1510d23b9b00SRafael J. Wysocki /**
1511d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
1512d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1513d23b9b00SRafael J. Wysocki  */
1514d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
1515d23b9b00SRafael J. Wysocki {
1516d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
1517d23b9b00SRafael J. Wysocki 
1518d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
1519d23b9b00SRafael J. Wysocki }
1520d23b9b00SRafael J. Wysocki 
1521d23b9b00SRafael J. Wysocki /**
1522d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
1523d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1524d23b9b00SRafael J. Wysocki  */
1525d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
1526d23b9b00SRafael J. Wysocki {
1527d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
1528d23b9b00SRafael J. Wysocki 
1529d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_noirq(dev);
1530d23b9b00SRafael J. Wysocki }
1531d23b9b00SRafael J. Wysocki 
1532d23b9b00SRafael J. Wysocki /**
1533d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
1534d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1535d23b9b00SRafael J. Wysocki  */
1536d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
1537d23b9b00SRafael J. Wysocki {
1538d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
1539d23b9b00SRafael J. Wysocki 
1540d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_noirq(dev);
1541d23b9b00SRafael J. Wysocki }
1542d23b9b00SRafael J. Wysocki 
1543d23b9b00SRafael J. Wysocki /**
1544d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
1545d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1546d23b9b00SRafael J. Wysocki  */
1547d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
1548d23b9b00SRafael J. Wysocki {
1549d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
1550d23b9b00SRafael J. Wysocki 
1551d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
1552d23b9b00SRafael J. Wysocki }
1553d23b9b00SRafael J. Wysocki 
15540f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
15550f1d6986SRafael J. Wysocki 
15560f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
15570f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
15580f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
15590f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
15600f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
15610f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
15620f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
15630f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
15640f1d6986SRafael J. Wysocki 
15650f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
15660f1d6986SRafael J. Wysocki 
1567d23b9b00SRafael J. Wysocki /**
1568f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1569f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1570f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1571f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1572f721889fSRafael J. Wysocki  */
1573f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1574f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1575f721889fSRafael J. Wysocki {
1576f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1577f721889fSRafael J. Wysocki 		return;
1578f721889fSRafael J. Wysocki 
15795063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
15805063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
1581f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1582f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1583f721889fSRafael J. Wysocki 	genpd->gov = gov;
1584f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1585f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1586c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
158717b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
158817b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1589c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1590c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1591596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1592596ba34bSRafael J. Wysocki 	genpd->suspended_count = 0;
1593221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
1594f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1595f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1596f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1597596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1598596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
1599596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1600596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
1601596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1602596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
1603596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1604596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
1605596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1606d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
1607d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
1608596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
1609d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
1610596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
1611ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
1612ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
1613c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
1614c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
1615c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
1616c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
1617d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
1618d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
1619d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
1620d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
16215125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
16225125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
16235125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
16245125bbf3SRafael J. Wysocki }
1625