xref: /openbmc/linux/drivers/base/power/domain.c (revision 80de3d7f)
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>
146ff7bb0dSRafael J. Wysocki #include <linux/pm_qos.h>
15f721889fSRafael J. Wysocki #include <linux/slab.h>
16f721889fSRafael J. Wysocki #include <linux/err.h>
1717b75ecaSRafael J. Wysocki #include <linux/sched.h>
1817b75ecaSRafael J. Wysocki #include <linux/suspend.h>
19d5e4cbfeSRafael J. Wysocki #include <linux/export.h>
20d5e4cbfeSRafael J. Wysocki 
21d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev)		\
22d5e4cbfeSRafael J. Wysocki ({								\
23d5e4cbfeSRafael J. Wysocki 	type (*__routine)(struct device *__d); 			\
24d5e4cbfeSRafael J. Wysocki 	type __ret = (type)0;					\
25d5e4cbfeSRafael J. Wysocki 								\
26d5e4cbfeSRafael J. Wysocki 	__routine = genpd->dev_ops.callback; 			\
27d5e4cbfeSRafael J. Wysocki 	if (__routine) {					\
28d5e4cbfeSRafael J. Wysocki 		__ret = __routine(dev); 			\
29d5e4cbfeSRafael J. Wysocki 	} else {						\
30d5e4cbfeSRafael J. Wysocki 		__routine = dev_gpd_data(dev)->ops.callback;	\
31d5e4cbfeSRafael J. Wysocki 		if (__routine) 					\
32d5e4cbfeSRafael J. Wysocki 			__ret = __routine(dev);			\
33d5e4cbfeSRafael J. Wysocki 	}							\
34d5e4cbfeSRafael J. Wysocki 	__ret;							\
35d5e4cbfeSRafael J. Wysocki })
36f721889fSRafael J. Wysocki 
370140d8bdSRafael J. Wysocki #define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name)	\
380140d8bdSRafael J. Wysocki ({										\
390140d8bdSRafael J. Wysocki 	ktime_t __start = ktime_get();						\
400140d8bdSRafael J. Wysocki 	type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);		\
410140d8bdSRafael J. Wysocki 	s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));		\
426ff7bb0dSRafael J. Wysocki 	struct gpd_timing_data *__td = &dev_gpd_data(dev)->td;			\
436ff7bb0dSRafael J. Wysocki 	if (!__retval && __elapsed > __td->field) {				\
446ff7bb0dSRafael J. Wysocki 		__td->field = __elapsed;					\
450140d8bdSRafael J. Wysocki 		dev_warn(dev, name " latency exceeded, new value %lld ns\n",	\
460140d8bdSRafael J. Wysocki 			__elapsed);						\
476ff7bb0dSRafael J. Wysocki 		genpd->max_off_time_changed = true;				\
486ff7bb0dSRafael J. Wysocki 		__td->constraint_changed = true;				\
490140d8bdSRafael J. Wysocki 	}									\
500140d8bdSRafael J. Wysocki 	__retval;								\
510140d8bdSRafael J. Wysocki })
520140d8bdSRafael J. Wysocki 
535125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list);
545125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock);
555125bbf3SRafael J. Wysocki 
565248051bSRafael J. Wysocki #ifdef CONFIG_PM
575248051bSRafael J. Wysocki 
58b02c999aSRafael J. Wysocki struct generic_pm_domain *dev_to_genpd(struct device *dev)
595248051bSRafael J. Wysocki {
605248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
615248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
625248051bSRafael J. Wysocki 
63596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
645248051bSRafael J. Wysocki }
65f721889fSRafael J. Wysocki 
66d5e4cbfeSRafael J. Wysocki static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
67d5e4cbfeSRafael J. Wysocki {
680140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev,
690140d8bdSRafael J. Wysocki 					stop_latency_ns, "stop");
70d5e4cbfeSRafael J. Wysocki }
71d5e4cbfeSRafael J. Wysocki 
72d5e4cbfeSRafael J. Wysocki static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
73d5e4cbfeSRafael J. Wysocki {
740140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev,
750140d8bdSRafael J. Wysocki 					start_latency_ns, "start");
76d5e4cbfeSRafael J. Wysocki }
77d5e4cbfeSRafael J. Wysocki 
78ecf00475SRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev)
79ecf00475SRafael J. Wysocki {
800140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev,
810140d8bdSRafael J. Wysocki 					save_state_latency_ns, "state save");
82ecf00475SRafael J. Wysocki }
83ecf00475SRafael J. Wysocki 
84ecf00475SRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev)
85ecf00475SRafael J. Wysocki {
860140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev,
870140d8bdSRafael J. Wysocki 					restore_state_latency_ns,
880140d8bdSRafael J. Wysocki 					"state restore");
89ecf00475SRafael J. Wysocki }
90ecf00475SRafael J. Wysocki 
91c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
92f721889fSRafael J. Wysocki {
93c4bb3160SRafael J. Wysocki 	bool ret = false;
94c4bb3160SRafael J. Wysocki 
95c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
96c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
97c4bb3160SRafael J. Wysocki 
98c4bb3160SRafael J. Wysocki 	return ret;
99c4bb3160SRafael J. Wysocki }
100c4bb3160SRafael J. Wysocki 
101c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
102c4bb3160SRafael J. Wysocki {
103c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
104c4bb3160SRafael J. Wysocki 	smp_mb__after_atomic_inc();
105f721889fSRafael J. Wysocki }
106f721889fSRafael J. Wysocki 
10717b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
10817b75ecaSRafael J. Wysocki {
10917b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
11017b75ecaSRafael J. Wysocki 
11117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
11217b75ecaSRafael J. Wysocki 	/*
11317b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
11417b75ecaSRafael J. Wysocki 	 * or the power off state.
11517b75ecaSRafael J. Wysocki 	 */
11617b75ecaSRafael J. Wysocki 	for (;;) {
11717b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
11817b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
119c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
120c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
12117b75ecaSRafael J. Wysocki 			break;
12217b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
12317b75ecaSRafael J. Wysocki 
12417b75ecaSRafael J. Wysocki 		schedule();
12517b75ecaSRafael J. Wysocki 
12617b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
12717b75ecaSRafael J. Wysocki 	}
12817b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
12917b75ecaSRafael J. Wysocki }
13017b75ecaSRafael J. Wysocki 
13117b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
13217b75ecaSRafael J. Wysocki {
13317b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
13417b75ecaSRafael J. Wysocki }
13517b75ecaSRafael J. Wysocki 
136c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
137c6d22b37SRafael J. Wysocki {
138c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
139c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
140c6d22b37SRafael J. Wysocki }
141c6d22b37SRafael J. Wysocki 
142f721889fSRafael J. Wysocki /**
1435063ce15SRafael J. Wysocki  * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
1445248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
1455248051bSRafael J. Wysocki  *
1465063ce15SRafael J. Wysocki  * Restore power to @genpd and all of its masters so that it is possible to
1475248051bSRafael J. Wysocki  * resume a device belonging to it.
1485248051bSRafael J. Wysocki  */
1493f241775SRafael J. Wysocki int __pm_genpd_poweron(struct generic_pm_domain *genpd)
1503f241775SRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
1515248051bSRafael J. Wysocki {
1525063ce15SRafael J. Wysocki 	struct gpd_link *link;
1533f241775SRafael J. Wysocki 	DEFINE_WAIT(wait);
1545248051bSRafael J. Wysocki 	int ret = 0;
1555248051bSRafael J. Wysocki 
1565063ce15SRafael J. Wysocki 	/* If the domain's master is being waited for, we have to wait too. */
1573f241775SRafael J. Wysocki 	for (;;) {
1583f241775SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
1593f241775SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
16017877eb5SRafael J. Wysocki 		if (genpd->status != GPD_STATE_WAIT_MASTER)
1613f241775SRafael J. Wysocki 			break;
1623f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1633f241775SRafael J. Wysocki 
1643f241775SRafael J. Wysocki 		schedule();
1653f241775SRafael J. Wysocki 
16617b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
1673f241775SRafael J. Wysocki 	}
1683f241775SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
16917b75ecaSRafael J. Wysocki 
17017b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
171596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
1723f241775SRafael J. Wysocki 		return 0;
1735248051bSRafael J. Wysocki 
174c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
175c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
1763f241775SRafael J. Wysocki 		return 0;
177c6d22b37SRafael J. Wysocki 	}
178c6d22b37SRafael J. Wysocki 
1795063ce15SRafael J. Wysocki 	/*
1805063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
1815063ce15SRafael J. Wysocki 	 * executed, unless one of the masters' .power_on() callbacks fiddles
1825063ce15SRafael J. Wysocki 	 * with it.
1835063ce15SRafael J. Wysocki 	 */
1845063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1855063ce15SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
18617877eb5SRafael J. Wysocki 		genpd->status = GPD_STATE_WAIT_MASTER;
1873c07cbc4SRafael J. Wysocki 
1885248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1895248051bSRafael J. Wysocki 
1905063ce15SRafael J. Wysocki 		ret = pm_genpd_poweron(link->master);
1919e08cf42SRafael J. Wysocki 
1929e08cf42SRafael J. Wysocki 		mutex_lock(&genpd->lock);
1939e08cf42SRafael J. Wysocki 
1943f241775SRafael J. Wysocki 		/*
1953f241775SRafael J. Wysocki 		 * The "wait for parent" status is guaranteed not to change
1965063ce15SRafael J. Wysocki 		 * while the master is powering on.
1973f241775SRafael J. Wysocki 		 */
1983f241775SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1993f241775SRafael J. Wysocki 		wake_up_all(&genpd->status_wait_queue);
2005063ce15SRafael J. Wysocki 		if (ret) {
2015063ce15SRafael J. Wysocki 			genpd_sd_counter_dec(link->master);
2029e08cf42SRafael J. Wysocki 			goto err;
2035248051bSRafael J. Wysocki 		}
2045063ce15SRafael J. Wysocki 	}
2055248051bSRafael J. Wysocki 
2069e08cf42SRafael J. Wysocki 	if (genpd->power_on) {
2070140d8bdSRafael J. Wysocki 		ktime_t time_start = ktime_get();
2080140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
2090140d8bdSRafael J. Wysocki 
210fe202fdeSRafael J. Wysocki 		ret = genpd->power_on(genpd);
2119e08cf42SRafael J. Wysocki 		if (ret)
2129e08cf42SRafael J. Wysocki 			goto err;
2130140d8bdSRafael J. Wysocki 
2140140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
215e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_on_latency_ns) {
2160140d8bdSRafael J. Wysocki 			genpd->power_on_latency_ns = elapsed_ns;
2176ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
218e84b2c20SRafael J. Wysocki 			if (genpd->name)
219e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-on latency exceeded, "
220e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
221e84b2c20SRafael J. Wysocki 					elapsed_ns);
222e84b2c20SRafael J. Wysocki 		}
2233c07cbc4SRafael J. Wysocki 	}
2245248051bSRafael J. Wysocki 
2259e08cf42SRafael J. Wysocki 	genpd_set_active(genpd);
2269e08cf42SRafael J. Wysocki 
2273f241775SRafael J. Wysocki 	return 0;
2289e08cf42SRafael J. Wysocki 
2299e08cf42SRafael J. Wysocki  err:
2305063ce15SRafael J. Wysocki 	list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
2315063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
2329e08cf42SRafael J. Wysocki 
2333f241775SRafael J. Wysocki 	return ret;
2343f241775SRafael J. Wysocki }
2353f241775SRafael J. Wysocki 
2363f241775SRafael J. Wysocki /**
2375063ce15SRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its masters.
2383f241775SRafael J. Wysocki  * @genpd: PM domain to power up.
2393f241775SRafael J. Wysocki  */
2403f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd)
2413f241775SRafael J. Wysocki {
2423f241775SRafael J. Wysocki 	int ret;
2433f241775SRafael J. Wysocki 
2443f241775SRafael J. Wysocki 	mutex_lock(&genpd->lock);
2453f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
2463f241775SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
2473f241775SRafael J. Wysocki 	return ret;
2485248051bSRafael J. Wysocki }
2495248051bSRafael J. Wysocki 
2505248051bSRafael J. Wysocki #endif /* CONFIG_PM */
2515248051bSRafael J. Wysocki 
2525248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
2535248051bSRafael J. Wysocki 
2546ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
2556ff7bb0dSRafael J. Wysocki 				     unsigned long val, void *ptr)
2566ff7bb0dSRafael J. Wysocki {
2576ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
2586ff7bb0dSRafael J. Wysocki 	struct device *dev;
2596ff7bb0dSRafael J. Wysocki 
2606ff7bb0dSRafael J. Wysocki 	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
2616ff7bb0dSRafael J. Wysocki 
2626ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
2636ff7bb0dSRafael J. Wysocki 	dev = gpd_data->base.dev;
2646ff7bb0dSRafael J. Wysocki 	if (!dev) {
2656ff7bb0dSRafael J. Wysocki 		mutex_unlock(&gpd_data->lock);
2666ff7bb0dSRafael J. Wysocki 		return NOTIFY_DONE;
2676ff7bb0dSRafael J. Wysocki 	}
2686ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
2696ff7bb0dSRafael J. Wysocki 
2706ff7bb0dSRafael J. Wysocki 	for (;;) {
2716ff7bb0dSRafael J. Wysocki 		struct generic_pm_domain *genpd;
2726ff7bb0dSRafael J. Wysocki 		struct pm_domain_data *pdd;
2736ff7bb0dSRafael J. Wysocki 
2746ff7bb0dSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
2756ff7bb0dSRafael J. Wysocki 
2766ff7bb0dSRafael J. Wysocki 		pdd = dev->power.subsys_data ?
2776ff7bb0dSRafael J. Wysocki 				dev->power.subsys_data->domain_data : NULL;
2786ff7bb0dSRafael J. Wysocki 		if (pdd) {
2796ff7bb0dSRafael J. Wysocki 			to_gpd_data(pdd)->td.constraint_changed = true;
2806ff7bb0dSRafael J. Wysocki 			genpd = dev_to_genpd(dev);
2816ff7bb0dSRafael J. Wysocki 		} else {
2826ff7bb0dSRafael J. Wysocki 			genpd = ERR_PTR(-ENODATA);
2836ff7bb0dSRafael J. Wysocki 		}
2846ff7bb0dSRafael J. Wysocki 
2856ff7bb0dSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
2866ff7bb0dSRafael J. Wysocki 
2876ff7bb0dSRafael J. Wysocki 		if (!IS_ERR(genpd)) {
2886ff7bb0dSRafael J. Wysocki 			mutex_lock(&genpd->lock);
2896ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
2906ff7bb0dSRafael J. Wysocki 			mutex_unlock(&genpd->lock);
2916ff7bb0dSRafael J. Wysocki 		}
2926ff7bb0dSRafael J. Wysocki 
2936ff7bb0dSRafael J. Wysocki 		dev = dev->parent;
2946ff7bb0dSRafael J. Wysocki 		if (!dev || dev->power.ignore_children)
2956ff7bb0dSRafael J. Wysocki 			break;
2966ff7bb0dSRafael J. Wysocki 	}
2976ff7bb0dSRafael J. Wysocki 
2986ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
2996ff7bb0dSRafael J. Wysocki }
3006ff7bb0dSRafael J. Wysocki 
3015248051bSRafael J. Wysocki /**
302f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
3034605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to save the state of.
304f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
305f721889fSRafael J. Wysocki  */
3064605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd,
307f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
30817b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
309f721889fSRafael J. Wysocki {
310cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
3114605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
312f721889fSRafael J. Wysocki 	int ret = 0;
313f721889fSRafael J. Wysocki 
314cd0ea672SRafael J. Wysocki 	if (gpd_data->need_restore)
315f721889fSRafael J. Wysocki 		return 0;
316f721889fSRafael J. Wysocki 
31717b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
31817b75ecaSRafael J. Wysocki 
319d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
320ecf00475SRafael J. Wysocki 	ret = genpd_save_dev(genpd, dev);
321d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
322f721889fSRafael J. Wysocki 
32317b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
32417b75ecaSRafael J. Wysocki 
325f721889fSRafael J. Wysocki 	if (!ret)
326cd0ea672SRafael J. Wysocki 		gpd_data->need_restore = true;
327f721889fSRafael J. Wysocki 
328f721889fSRafael J. Wysocki 	return ret;
329f721889fSRafael J. Wysocki }
330f721889fSRafael J. Wysocki 
331f721889fSRafael J. Wysocki /**
332f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
3334605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to restore the state of.
334f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
335f721889fSRafael J. Wysocki  */
3364605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
337f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
33817b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
339f721889fSRafael J. Wysocki {
340cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
3414605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
34280de3d7fSRafael J. Wysocki 	bool need_restore = gpd_data->need_restore;
343f721889fSRafael J. Wysocki 
34480de3d7fSRafael J. Wysocki 	gpd_data->need_restore = false;
34517b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
34617b75ecaSRafael J. Wysocki 
347d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
34880de3d7fSRafael J. Wysocki 	if (need_restore)
349ecf00475SRafael J. Wysocki 		genpd_restore_dev(genpd, dev);
350f721889fSRafael J. Wysocki 
35117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
352f721889fSRafael J. Wysocki }
353f721889fSRafael J. Wysocki 
354f721889fSRafael J. Wysocki /**
355c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
356c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
357c6d22b37SRafael J. Wysocki  *
358c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
359c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
360c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
361c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
362c6d22b37SRafael J. Wysocki  */
363c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
364c6d22b37SRafael J. Wysocki {
36517877eb5SRafael J. Wysocki 	return genpd->status == GPD_STATE_WAIT_MASTER
3663f241775SRafael J. Wysocki 		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
367c6d22b37SRafael J. Wysocki }
368c6d22b37SRafael J. Wysocki 
369c6d22b37SRafael J. Wysocki /**
37056375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
37156375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
37256375fd4SRafael J. Wysocki  *
37356375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
37456375fd4SRafael J. Wysocki  * before.
37556375fd4SRafael J. Wysocki  */
3760bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
37756375fd4SRafael J. Wysocki {
37856375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
37956375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
38056375fd4SRafael J. Wysocki }
38156375fd4SRafael J. Wysocki 
38256375fd4SRafael J. Wysocki /**
383f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
384f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
385f721889fSRafael J. Wysocki  *
386f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
387f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
388f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
389f721889fSRafael J. Wysocki  */
390f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
39117b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
392f721889fSRafael J. Wysocki {
3934605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
3945063ce15SRafael J. Wysocki 	struct gpd_link *link;
395f721889fSRafael J. Wysocki 	unsigned int not_suspended;
396c6d22b37SRafael J. Wysocki 	int ret = 0;
397f721889fSRafael J. Wysocki 
398c6d22b37SRafael J. Wysocki  start:
399c6d22b37SRafael J. Wysocki 	/*
400c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
401c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
4025063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
403c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
4043f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
405c6d22b37SRafael J. Wysocki 	 */
4063f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
40717877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
4083f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
409f721889fSRafael J. Wysocki 		return 0;
410f721889fSRafael J. Wysocki 
411c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
412f721889fSRafael J. Wysocki 		return -EBUSY;
413f721889fSRafael J. Wysocki 
414f721889fSRafael J. Wysocki 	not_suspended = 0;
4154605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
4160aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
4171e78a0c7SRafael J. Wysocki 		    || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on))
418f721889fSRafael J. Wysocki 			not_suspended++;
419f721889fSRafael J. Wysocki 
420f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
421f721889fSRafael J. Wysocki 		return -EBUSY;
422f721889fSRafael J. Wysocki 
423c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
424c6d22b37SRafael J. Wysocki 		/*
425c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
426c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
427c6d22b37SRafael J. Wysocki 		 */
428c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
429c6d22b37SRafael J. Wysocki 		return 0;
430c6d22b37SRafael J. Wysocki 	}
431c6d22b37SRafael J. Wysocki 
432f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
433f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
434f721889fSRafael J. Wysocki 			return -EAGAIN;
435f721889fSRafael J. Wysocki 	}
436f721889fSRafael J. Wysocki 
43717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
438c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
43917b75ecaSRafael J. Wysocki 
4404605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
4413c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
4424605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
4433f241775SRafael J. Wysocki 
4443f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
4453f241775SRafael J. Wysocki 			goto out;
4463f241775SRafael J. Wysocki 
447697a7f37SRafael J. Wysocki 		if (ret) {
448697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
449697a7f37SRafael J. Wysocki 			goto out;
450697a7f37SRafael J. Wysocki 		}
451f721889fSRafael J. Wysocki 
452c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
453c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
454c6d22b37SRafael J. Wysocki 			goto start;
455c6d22b37SRafael J. Wysocki 		}
456c6d22b37SRafael J. Wysocki 	}
45717b75ecaSRafael J. Wysocki 
4583c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
4590140d8bdSRafael J. Wysocki 		ktime_t time_start;
4600140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
4610140d8bdSRafael J. Wysocki 
4623c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
4633c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
464c6d22b37SRafael J. Wysocki 			goto out;
465c6d22b37SRafael J. Wysocki 		}
46617b75ecaSRafael J. Wysocki 
4670140d8bdSRafael J. Wysocki 		time_start = ktime_get();
4680140d8bdSRafael J. Wysocki 
4693c07cbc4SRafael J. Wysocki 		/*
4705063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
4715063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
4723c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
4733c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
4743c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
4753c07cbc4SRafael J. Wysocki 		 * happen very often).
4763c07cbc4SRafael J. Wysocki 		 */
477d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
478d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
479d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
480d2805402SRafael J. Wysocki 			goto out;
481d2805402SRafael J. Wysocki 		}
4820140d8bdSRafael J. Wysocki 
4830140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
484e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_off_latency_ns) {
4850140d8bdSRafael J. Wysocki 			genpd->power_off_latency_ns = elapsed_ns;
4866ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
487e84b2c20SRafael J. Wysocki 			if (genpd->name)
488e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-off latency exceeded, "
489e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
490e84b2c20SRafael J. Wysocki 					elapsed_ns);
491e84b2c20SRafael J. Wysocki 		}
492d2805402SRafael J. Wysocki 	}
493f721889fSRafael J. Wysocki 
49417b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
495221e9b58SRafael J. Wysocki 
4965063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
4975063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
4985063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
4995063ce15SRafael J. Wysocki 	}
50017b75ecaSRafael J. Wysocki 
501c6d22b37SRafael J. Wysocki  out:
502c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
503c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
504c6d22b37SRafael J. Wysocki 	return ret;
505f721889fSRafael J. Wysocki }
506f721889fSRafael J. Wysocki 
507f721889fSRafael J. Wysocki /**
508f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
509f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
510f721889fSRafael J. Wysocki  */
511f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
512f721889fSRafael J. Wysocki {
513f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
514f721889fSRafael J. Wysocki 
515f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
516f721889fSRafael J. Wysocki 
51717b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
518f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
51917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
520f721889fSRafael J. Wysocki }
521f721889fSRafael J. Wysocki 
522f721889fSRafael J. Wysocki /**
523f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
524f721889fSRafael J. Wysocki  * @dev: Device to suspend.
525f721889fSRafael J. Wysocki  *
526f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
527f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
528f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
529f721889fSRafael J. Wysocki  */
530f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
531f721889fSRafael J. Wysocki {
532f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
533b02c999aSRafael J. Wysocki 	bool (*stop_ok)(struct device *__dev);
534d5e4cbfeSRafael J. Wysocki 	int ret;
535f721889fSRafael J. Wysocki 
536f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
537f721889fSRafael J. Wysocki 
5385248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5395248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
540f721889fSRafael J. Wysocki 		return -EINVAL;
541f721889fSRafael J. Wysocki 
5420aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5430aa2a221SRafael J. Wysocki 
5441e78a0c7SRafael J. Wysocki 	if (dev_gpd_data(dev)->always_on)
5451e78a0c7SRafael J. Wysocki 		return -EBUSY;
5461e78a0c7SRafael J. Wysocki 
547b02c999aSRafael J. Wysocki 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
548b02c999aSRafael J. Wysocki 	if (stop_ok && !stop_ok(dev))
549b02c999aSRafael J. Wysocki 		return -EBUSY;
550b02c999aSRafael J. Wysocki 
551d5e4cbfeSRafael J. Wysocki 	ret = genpd_stop_dev(genpd, dev);
552f721889fSRafael J. Wysocki 	if (ret)
55317b75ecaSRafael J. Wysocki 		return ret;
55417b75ecaSRafael J. Wysocki 
5550aa2a221SRafael J. Wysocki 	/*
5560aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
5570aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
5580aa2a221SRafael J. Wysocki 	 */
5590aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
5600aa2a221SRafael J. Wysocki 		return 0;
5610aa2a221SRafael J. Wysocki 
562c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
563f721889fSRafael J. Wysocki 	genpd->in_progress++;
564f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
565f721889fSRafael J. Wysocki 	genpd->in_progress--;
566c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
567f721889fSRafael J. Wysocki 
568f721889fSRafael J. Wysocki 	return 0;
569f721889fSRafael J. Wysocki }
570f721889fSRafael J. Wysocki 
571f721889fSRafael J. Wysocki /**
572f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
573f721889fSRafael J. Wysocki  * @dev: Device to resume.
574f721889fSRafael J. Wysocki  *
575f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
576f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
577f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
578f721889fSRafael J. Wysocki  */
579f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
580f721889fSRafael J. Wysocki {
581f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
582c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
583f721889fSRafael J. Wysocki 	int ret;
584f721889fSRafael J. Wysocki 
585f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
586f721889fSRafael J. Wysocki 
5875248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5885248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
589f721889fSRafael J. Wysocki 		return -EINVAL;
590f721889fSRafael J. Wysocki 
5910aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5920aa2a221SRafael J. Wysocki 
5930aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
5940aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
59580de3d7fSRafael J. Wysocki 		return genpd_start_dev(genpd, dev);
5960aa2a221SRafael J. Wysocki 
597c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
5983f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
5993f241775SRafael J. Wysocki 	if (ret) {
6003f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
6013f241775SRafael J. Wysocki 		return ret;
6023f241775SRafael J. Wysocki 	}
60317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
604c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
605c6d22b37SRafael J. Wysocki 	for (;;) {
606c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
607c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
608c6d22b37SRafael J. Wysocki 		/*
609c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
610c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
611c6d22b37SRafael J. Wysocki 		 * not wait.
612c6d22b37SRafael J. Wysocki 		 */
613c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
614c6d22b37SRafael J. Wysocki 			break;
615c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
616c6d22b37SRafael J. Wysocki 
617c6d22b37SRafael J. Wysocki 		schedule();
618c6d22b37SRafael J. Wysocki 
619c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
620c6d22b37SRafael J. Wysocki 	}
621c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
622cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
623c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
624c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
62517b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
626c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
62717b75ecaSRafael J. Wysocki 
628f721889fSRafael J. Wysocki 	return 0;
629f721889fSRafael J. Wysocki }
630f721889fSRafael J. Wysocki 
63117f2ae7fSRafael J. Wysocki /**
63217f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
63317f2ae7fSRafael J. Wysocki  */
63417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
63517f2ae7fSRafael J. Wysocki {
63617f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
63717f2ae7fSRafael J. Wysocki 
63817f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
63917f2ae7fSRafael J. Wysocki 
64017f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
64117f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
64217f2ae7fSRafael J. Wysocki 
64317f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
64417f2ae7fSRafael J. Wysocki }
64517f2ae7fSRafael J. Wysocki 
646f721889fSRafael J. Wysocki #else
647f721889fSRafael J. Wysocki 
6486ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
6496ff7bb0dSRafael J. Wysocki 					    unsigned long val, void *ptr)
6506ff7bb0dSRafael J. Wysocki {
6516ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
6526ff7bb0dSRafael J. Wysocki }
6536ff7bb0dSRafael J. Wysocki 
654f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
655f721889fSRafael J. Wysocki 
656f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
657f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
658f721889fSRafael J. Wysocki 
659f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
660f721889fSRafael J. Wysocki 
661596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
662596ba34bSRafael J. Wysocki 
663d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
664d5e4cbfeSRafael J. Wysocki 				    struct device *dev)
665d5e4cbfeSRafael J. Wysocki {
666d5e4cbfeSRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
667d5e4cbfeSRafael J. Wysocki }
668d5e4cbfeSRafael J. Wysocki 
669d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
670d23b9b00SRafael J. Wysocki {
671d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
672d23b9b00SRafael J. Wysocki }
673d23b9b00SRafael J. Wysocki 
674d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
675d23b9b00SRafael J. Wysocki {
676d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
677d23b9b00SRafael J. Wysocki }
678d23b9b00SRafael J. Wysocki 
679d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
680d23b9b00SRafael J. Wysocki {
681d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
682d23b9b00SRafael J. Wysocki }
683d23b9b00SRafael J. Wysocki 
684d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
685d23b9b00SRafael J. Wysocki {
686d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
687d23b9b00SRafael J. Wysocki }
688d23b9b00SRafael J. Wysocki 
689d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
690d23b9b00SRafael J. Wysocki {
691d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
692d23b9b00SRafael J. Wysocki }
693d23b9b00SRafael J. Wysocki 
694d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
695d23b9b00SRafael J. Wysocki {
696d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
697d23b9b00SRafael J. Wysocki }
698d23b9b00SRafael J. Wysocki 
699d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
700d23b9b00SRafael J. Wysocki {
701d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
702d23b9b00SRafael J. Wysocki }
703d23b9b00SRafael J. Wysocki 
704d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
705d23b9b00SRafael J. Wysocki {
706d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
707d23b9b00SRafael J. Wysocki }
708d23b9b00SRafael J. Wysocki 
709596ba34bSRafael J. Wysocki /**
7105063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
711596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
712596ba34bSRafael J. Wysocki  *
713596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
7145063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
715596ba34bSRafael J. Wysocki  *
716596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
717596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
718596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
719596ba34bSRafael J. Wysocki  */
720596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
721596ba34bSRafael J. Wysocki {
7225063ce15SRafael J. Wysocki 	struct gpd_link *link;
723596ba34bSRafael J. Wysocki 
72417b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
725596ba34bSRafael J. Wysocki 		return;
726596ba34bSRafael J. Wysocki 
727c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
728c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
729596ba34bSRafael J. Wysocki 		return;
730596ba34bSRafael J. Wysocki 
731596ba34bSRafael J. Wysocki 	if (genpd->power_off)
732596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
733596ba34bSRafael J. Wysocki 
73417b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
7355063ce15SRafael J. Wysocki 
7365063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
7375063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
7385063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
739596ba34bSRafael J. Wysocki 	}
740596ba34bSRafael J. Wysocki }
741596ba34bSRafael J. Wysocki 
742596ba34bSRafael J. Wysocki /**
7434ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
7444ecd6e65SRafael J. Wysocki  * @dev: Device to check.
7454ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
7464ecd6e65SRafael J. Wysocki  *
7474ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
7484ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
7494ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
7504ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
7514ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
7524ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
7534ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
7544ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
7554ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
7564ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
7574ecd6e65SRafael J. Wysocki  */
7584ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
7594ecd6e65SRafael J. Wysocki {
7604ecd6e65SRafael J. Wysocki 	bool active_wakeup;
7614ecd6e65SRafael J. Wysocki 
7624ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
7634ecd6e65SRafael J. Wysocki 		return false;
7644ecd6e65SRafael J. Wysocki 
765d5e4cbfeSRafael J. Wysocki 	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
7664ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
7674ecd6e65SRafael J. Wysocki }
7684ecd6e65SRafael J. Wysocki 
7694ecd6e65SRafael J. Wysocki /**
770596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
771596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
772596ba34bSRafael J. Wysocki  *
773596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
774596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
775596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
776596ba34bSRafael J. Wysocki  * consisting of I/O devices.
777596ba34bSRafael J. Wysocki  */
778596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
779596ba34bSRafael J. Wysocki {
780596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
781b6c10c84SRafael J. Wysocki 	int ret;
782596ba34bSRafael J. Wysocki 
783596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
784596ba34bSRafael J. Wysocki 
785596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
786596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
787596ba34bSRafael J. Wysocki 		return -EINVAL;
788596ba34bSRafael J. Wysocki 
78917b75ecaSRafael J. Wysocki 	/*
79017b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
79117b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
79217b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
79317b75ecaSRafael J. Wysocki 	 */
79417b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
79517b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
79617b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
79717b75ecaSRafael J. Wysocki 
79817b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
79917b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
80017b75ecaSRafael J. Wysocki 		return -EBUSY;
80117b75ecaSRafael J. Wysocki 	}
80217b75ecaSRafael J. Wysocki 
8034ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
8044ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
8054ecd6e65SRafael J. Wysocki 
80617b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
807596ba34bSRafael J. Wysocki 
80865533bbfSRafael J. Wysocki 	if (genpd->prepared_count++ == 0) {
80965533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
81017b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
81165533bbfSRafael J. Wysocki 	}
81217b75ecaSRafael J. Wysocki 
81317b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
814596ba34bSRafael J. Wysocki 
815596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
81617b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
817596ba34bSRafael J. Wysocki 		return 0;
818596ba34bSRafael J. Wysocki 	}
819596ba34bSRafael J. Wysocki 
820596ba34bSRafael J. Wysocki 	/*
82117b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
82217b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
823d5e4cbfeSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
82417b75ecaSRafael J. Wysocki 	 * to make it operational.
825596ba34bSRafael J. Wysocki 	 */
82617b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
827596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
828596ba34bSRafael J. Wysocki 
829b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
830b6c10c84SRafael J. Wysocki 	if (ret) {
831b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
832b6c10c84SRafael J. Wysocki 
833b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
834b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
835b6c10c84SRafael J. Wysocki 
836b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
83717b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
838b6c10c84SRafael J. Wysocki 	}
83917b75ecaSRafael J. Wysocki 
84017b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
841b6c10c84SRafael J. Wysocki 	return ret;
842596ba34bSRafael J. Wysocki }
843596ba34bSRafael J. Wysocki 
844596ba34bSRafael J. Wysocki /**
845596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
846596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
847596ba34bSRafael J. Wysocki  *
848596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
849596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
850596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
851596ba34bSRafael J. Wysocki  */
852596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
853596ba34bSRafael J. Wysocki {
854596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
855596ba34bSRafael J. Wysocki 
856596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
857596ba34bSRafael J. Wysocki 
858596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
859596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
860596ba34bSRafael J. Wysocki 		return -EINVAL;
861596ba34bSRafael J. Wysocki 
862d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
863596ba34bSRafael J. Wysocki }
864596ba34bSRafael J. Wysocki 
865596ba34bSRafael J. Wysocki /**
8660496c8aeSRafael J. Wysocki  * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
867596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
868596ba34bSRafael J. Wysocki  *
869596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
870596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
871596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
872596ba34bSRafael J. Wysocki  */
8730496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(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 
8830496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
8840496c8aeSRafael J. Wysocki }
885596ba34bSRafael J. Wysocki 
8860496c8aeSRafael J. Wysocki /**
8870496c8aeSRafael J. Wysocki  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
8880496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
8890496c8aeSRafael J. Wysocki  *
8900496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
8910496c8aeSRafael J. Wysocki  * been stopped.
8920496c8aeSRafael J. Wysocki  */
8930496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
8940496c8aeSRafael J. Wysocki {
8950496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
896596ba34bSRafael J. Wysocki 
8970496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
8980496c8aeSRafael J. Wysocki 
8990496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9000496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9010496c8aeSRafael J. Wysocki 		return -EINVAL;
9020496c8aeSRafael J. Wysocki 
9031e78a0c7SRafael J. Wysocki 	if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
9040496c8aeSRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
905d4f2d87aSRafael J. Wysocki 		return 0;
906d4f2d87aSRafael J. Wysocki 
907d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
908596ba34bSRafael J. Wysocki 
909596ba34bSRafael J. Wysocki 	/*
910596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
911596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
912596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
913596ba34bSRafael J. Wysocki 	 */
914596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
915596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
916596ba34bSRafael J. Wysocki 
917596ba34bSRafael J. Wysocki 	return 0;
918596ba34bSRafael J. Wysocki }
919596ba34bSRafael J. Wysocki 
920596ba34bSRafael J. Wysocki /**
9210496c8aeSRafael J. Wysocki  * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
922596ba34bSRafael J. Wysocki  * @dev: Device to resume.
923596ba34bSRafael J. Wysocki  *
9240496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
925596ba34bSRafael J. Wysocki  */
926596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
927596ba34bSRafael J. Wysocki {
928596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
929596ba34bSRafael J. Wysocki 
930596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
931596ba34bSRafael J. Wysocki 
932596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
933596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
934596ba34bSRafael J. Wysocki 		return -EINVAL;
935596ba34bSRafael J. Wysocki 
9361e78a0c7SRafael J. Wysocki 	if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
937cc85b207SRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
938596ba34bSRafael J. Wysocki 		return 0;
939596ba34bSRafael J. Wysocki 
940596ba34bSRafael J. Wysocki 	/*
941596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
942596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
943596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
944596ba34bSRafael J. Wysocki 	 */
945596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
946596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
947596ba34bSRafael J. Wysocki 
9480496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
949596ba34bSRafael J. Wysocki }
950596ba34bSRafael J. Wysocki 
951596ba34bSRafael J. Wysocki /**
9520496c8aeSRafael J. Wysocki  * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
9530496c8aeSRafael J. Wysocki  * @dev: Device to resume.
9540496c8aeSRafael J. Wysocki  *
9550496c8aeSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
9560496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
9570496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
9580496c8aeSRafael J. Wysocki  * devices.
9590496c8aeSRafael J. Wysocki  */
9600496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev)
9610496c8aeSRafael J. Wysocki {
9620496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
9630496c8aeSRafael J. Wysocki 
9640496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
9650496c8aeSRafael J. Wysocki 
9660496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9670496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9680496c8aeSRafael J. Wysocki 		return -EINVAL;
9690496c8aeSRafael J. Wysocki 
9700496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
9710496c8aeSRafael J. Wysocki }
9720496c8aeSRafael J. Wysocki 
9730496c8aeSRafael J. Wysocki /**
9740496c8aeSRafael J. Wysocki  * pm_genpd_resume - Resume of device in an I/O PM domain.
975596ba34bSRafael J. Wysocki  * @dev: Device to resume.
976596ba34bSRafael J. Wysocki  *
977596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
978596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
979596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
980596ba34bSRafael J. Wysocki  */
981596ba34bSRafael J. Wysocki static int pm_genpd_resume(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 
991d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
992596ba34bSRafael J. Wysocki }
993596ba34bSRafael J. Wysocki 
994596ba34bSRafael J. Wysocki /**
9950496c8aeSRafael J. Wysocki  * pm_genpd_freeze - Freezing a device in an I/O PM domain.
996596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
997596ba34bSRafael J. Wysocki  *
998596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
999596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1000596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1001596ba34bSRafael J. Wysocki  */
1002596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
1003596ba34bSRafael J. Wysocki {
1004596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1005596ba34bSRafael J. Wysocki 
1006596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1007596ba34bSRafael J. Wysocki 
1008596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1009596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1010596ba34bSRafael J. Wysocki 		return -EINVAL;
1011596ba34bSRafael J. Wysocki 
1012d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
1013596ba34bSRafael J. Wysocki }
1014596ba34bSRafael J. Wysocki 
1015596ba34bSRafael J. Wysocki /**
10160496c8aeSRafael J. Wysocki  * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
10170496c8aeSRafael J. Wysocki  * @dev: Device to freeze.
10180496c8aeSRafael J. Wysocki  *
10190496c8aeSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
10200496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10210496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10220496c8aeSRafael J. Wysocki  * devices.
10230496c8aeSRafael J. Wysocki  */
10240496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev)
10250496c8aeSRafael J. Wysocki {
10260496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
10270496c8aeSRafael J. Wysocki 
10280496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10290496c8aeSRafael J. Wysocki 
10300496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10310496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10320496c8aeSRafael J. Wysocki 		return -EINVAL;
10330496c8aeSRafael J. Wysocki 
10340496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
10350496c8aeSRafael J. Wysocki }
10360496c8aeSRafael J. Wysocki 
10370496c8aeSRafael J. Wysocki /**
10380496c8aeSRafael J. Wysocki  * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1039596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1040596ba34bSRafael J. Wysocki  *
1041596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1042596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1043596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1044596ba34bSRafael J. Wysocki  * devices.
1045596ba34bSRafael J. Wysocki  */
1046596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
1047596ba34bSRafael J. Wysocki {
1048596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1049596ba34bSRafael J. Wysocki 
1050596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1051596ba34bSRafael J. Wysocki 
1052596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1053596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1054596ba34bSRafael J. Wysocki 		return -EINVAL;
1055596ba34bSRafael J. Wysocki 
10561e78a0c7SRafael J. Wysocki 	return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
10571e78a0c7SRafael J. Wysocki 		0 : genpd_stop_dev(genpd, dev);
1058596ba34bSRafael J. Wysocki }
1059596ba34bSRafael J. Wysocki 
1060596ba34bSRafael J. Wysocki /**
10610496c8aeSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1062596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1063596ba34bSRafael J. Wysocki  *
10640496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
10650496c8aeSRafael J. Wysocki  * before the system transition.
1066596ba34bSRafael J. Wysocki  */
1067596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
1068596ba34bSRafael J. Wysocki {
1069596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1070596ba34bSRafael J. Wysocki 
1071596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1072596ba34bSRafael J. Wysocki 
1073596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1074596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1075596ba34bSRafael J. Wysocki 		return -EINVAL;
1076596ba34bSRafael J. Wysocki 
10771e78a0c7SRafael J. Wysocki 	return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
10781e78a0c7SRafael J. Wysocki 		0 : genpd_start_dev(genpd, dev);
10790496c8aeSRafael J. Wysocki }
1080596ba34bSRafael J. Wysocki 
10810496c8aeSRafael J. Wysocki /**
10820496c8aeSRafael J. Wysocki  * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
10830496c8aeSRafael J. Wysocki  * @dev: Device to thaw.
10840496c8aeSRafael J. Wysocki  *
10850496c8aeSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
10860496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10870496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10880496c8aeSRafael J. Wysocki  * devices.
10890496c8aeSRafael J. Wysocki  */
10900496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev)
10910496c8aeSRafael J. Wysocki {
10920496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1093596ba34bSRafael J. Wysocki 
10940496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10950496c8aeSRafael J. Wysocki 
10960496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10970496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10980496c8aeSRafael J. Wysocki 		return -EINVAL;
10990496c8aeSRafael J. Wysocki 
11000496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
1101596ba34bSRafael J. Wysocki }
1102596ba34bSRafael J. Wysocki 
1103596ba34bSRafael J. Wysocki /**
1104596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1105596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1106596ba34bSRafael J. Wysocki  *
1107596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1108596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1109596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1110596ba34bSRafael J. Wysocki  */
1111596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1112596ba34bSRafael J. Wysocki {
1113596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1114596ba34bSRafael J. Wysocki 
1115596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1116596ba34bSRafael J. Wysocki 
1117596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1118596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1119596ba34bSRafael J. Wysocki 		return -EINVAL;
1120596ba34bSRafael J. Wysocki 
1121d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1122596ba34bSRafael J. Wysocki }
1123596ba34bSRafael J. Wysocki 
1124596ba34bSRafael J. Wysocki /**
11250496c8aeSRafael J. Wysocki  * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1126596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1127596ba34bSRafael J. Wysocki  *
11280496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
11290496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1130596ba34bSRafael J. Wysocki  */
1131596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1132596ba34bSRafael J. Wysocki {
1133596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1134596ba34bSRafael J. Wysocki 
1135596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1136596ba34bSRafael J. Wysocki 
1137596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1138596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1139596ba34bSRafael J. Wysocki 		return -EINVAL;
1140596ba34bSRafael J. Wysocki 
1141596ba34bSRafael J. Wysocki 	/*
1142596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1143596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1144596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
114565533bbfSRafael J. Wysocki 	 *
114665533bbfSRafael J. Wysocki 	 * At this point suspended_count == 0 means we are being run for the
114765533bbfSRafael J. Wysocki 	 * first time for the given domain in the present cycle.
114865533bbfSRafael J. Wysocki 	 */
114965533bbfSRafael J. Wysocki 	if (genpd->suspended_count++ == 0) {
115065533bbfSRafael J. Wysocki 		/*
115165533bbfSRafael J. Wysocki 		 * The boot kernel might put the domain into arbitrary state,
115265533bbfSRafael J. Wysocki 		 * so make it appear as powered off to pm_genpd_poweron(), so
115365533bbfSRafael J. Wysocki 		 * that it tries to power it on in case it was really off.
1154596ba34bSRafael J. Wysocki 		 */
115517b75ecaSRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1156596ba34bSRafael J. Wysocki 		if (genpd->suspend_power_off) {
1157596ba34bSRafael J. Wysocki 			/*
115865533bbfSRafael J. Wysocki 			 * If the domain was off before the hibernation, make
115965533bbfSRafael J. Wysocki 			 * sure it will be off going forward.
1160596ba34bSRafael J. Wysocki 			 */
1161596ba34bSRafael J. Wysocki 			if (genpd->power_off)
1162596ba34bSRafael J. Wysocki 				genpd->power_off(genpd);
116365533bbfSRafael J. Wysocki 
1164596ba34bSRafael J. Wysocki 			return 0;
1165596ba34bSRafael J. Wysocki 		}
116665533bbfSRafael J. Wysocki 	}
1167596ba34bSRafael J. Wysocki 
116818dd2eceSRafael J. Wysocki 	if (genpd->suspend_power_off)
116918dd2eceSRafael J. Wysocki 		return 0;
117018dd2eceSRafael J. Wysocki 
1171596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
1172596ba34bSRafael J. Wysocki 
11731e78a0c7SRafael J. Wysocki 	return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev);
1174596ba34bSRafael J. Wysocki }
1175596ba34bSRafael J. Wysocki 
1176596ba34bSRafael J. Wysocki /**
1177596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1178596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1179596ba34bSRafael J. Wysocki  *
1180596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1181596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1182596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1183596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1184596ba34bSRafael J. Wysocki  */
1185596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1186596ba34bSRafael J. Wysocki {
1187596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1188596ba34bSRafael J. Wysocki 	bool run_complete;
1189596ba34bSRafael J. Wysocki 
1190596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1191596ba34bSRafael J. Wysocki 
1192596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1193596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1194596ba34bSRafael J. Wysocki 		return;
1195596ba34bSRafael J. Wysocki 
1196596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1197596ba34bSRafael J. Wysocki 
1198596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1199596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1200596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1201596ba34bSRafael J. Wysocki 
1202596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1203596ba34bSRafael J. Wysocki 
1204596ba34bSRafael J. Wysocki 	if (run_complete) {
1205596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
12066f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1207596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
12086f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1209596ba34bSRafael J. Wysocki 	}
1210596ba34bSRafael J. Wysocki }
1211596ba34bSRafael J. Wysocki 
1212596ba34bSRafael J. Wysocki #else
1213596ba34bSRafael J. Wysocki 
1214596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1215596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
12160496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late		NULL
1217596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
12180496c8aeSRafael J. Wysocki #define pm_genpd_resume_early		NULL
1219596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1220596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1221596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
12220496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late		NULL
1223596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
12240496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early		NULL
1225596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1226596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1227596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1228596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1229596ba34bSRafael J. Wysocki 
1230596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1231596ba34bSRafael J. Wysocki 
1232f721889fSRafael J. Wysocki /**
1233b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1234f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1235f721889fSRafael J. Wysocki  * @dev: Device to be added.
1236b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1237f721889fSRafael J. Wysocki  */
1238b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1239b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1240f721889fSRafael J. Wysocki {
1241cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
12424605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1243f721889fSRafael J. Wysocki 	int ret = 0;
1244f721889fSRafael J. Wysocki 
1245f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1246f721889fSRafael J. Wysocki 
1247f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1248f721889fSRafael J. Wysocki 		return -EINVAL;
1249f721889fSRafael J. Wysocki 
12506ff7bb0dSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
12516ff7bb0dSRafael J. Wysocki 	if (!gpd_data)
12526ff7bb0dSRafael J. Wysocki 		return -ENOMEM;
12536ff7bb0dSRafael J. Wysocki 
12546ff7bb0dSRafael J. Wysocki 	mutex_init(&gpd_data->lock);
12556ff7bb0dSRafael J. Wysocki 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
12566ff7bb0dSRafael J. Wysocki 	dev_pm_qos_add_notifier(dev, &gpd_data->nb);
12576ff7bb0dSRafael J. Wysocki 
125817b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1259f721889fSRafael J. Wysocki 
1260596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1261596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1262596ba34bSRafael J. Wysocki 		goto out;
1263596ba34bSRafael J. Wysocki 	}
1264596ba34bSRafael J. Wysocki 
12654605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
12664605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1267f721889fSRafael J. Wysocki 			ret = -EINVAL;
1268f721889fSRafael J. Wysocki 			goto out;
1269f721889fSRafael J. Wysocki 		}
1270f721889fSRafael J. Wysocki 
1271596ba34bSRafael J. Wysocki 	genpd->device_count++;
12726ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1273f721889fSRafael J. Wysocki 
12744605ab65SRafael J. Wysocki 	dev_pm_get_subsys_data(dev);
12756ff7bb0dSRafael J. Wysocki 
12766ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
12776ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
12786ff7bb0dSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
1279cd0ea672SRafael J. Wysocki 	dev->power.subsys_data->domain_data = &gpd_data->base;
1280cd0ea672SRafael J. Wysocki 	gpd_data->base.dev = dev;
1281cd0ea672SRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1282ca1d72f0SRafael J. Wysocki 	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
1283b02c999aSRafael J. Wysocki 	if (td)
1284b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1285f721889fSRafael J. Wysocki 
12866ff7bb0dSRafael J. Wysocki 	gpd_data->td.constraint_changed = true;
12876ff7bb0dSRafael J. Wysocki 	gpd_data->td.effective_constraint_ns = -1;
12886ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
12896ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
12906ff7bb0dSRafael J. Wysocki 
12916ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
12926ff7bb0dSRafael J. Wysocki 
12936ff7bb0dSRafael J. Wysocki 	return 0;
12946ff7bb0dSRafael J. Wysocki 
1295f721889fSRafael J. Wysocki  out:
129617b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1297f721889fSRafael J. Wysocki 
12986ff7bb0dSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
12996ff7bb0dSRafael J. Wysocki 	kfree(gpd_data);
1300f721889fSRafael J. Wysocki 	return ret;
1301f721889fSRafael J. Wysocki }
1302f721889fSRafael J. Wysocki 
1303f721889fSRafael J. Wysocki /**
1304c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1305c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1306c8aa130bSThomas Abraham  *   the device is added to.
1307c8aa130bSThomas Abraham  * @dev: Device to be added.
1308c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1309c8aa130bSThomas Abraham  */
1310c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1311c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1312c8aa130bSThomas Abraham {
1313c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1314c8aa130bSThomas Abraham 
1315c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1316c8aa130bSThomas Abraham 
1317c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1318c8aa130bSThomas Abraham 		return -EINVAL;
1319c8aa130bSThomas Abraham 
1320c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1321c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1322c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1323c8aa130bSThomas Abraham 			genpd = gpd;
1324c8aa130bSThomas Abraham 			break;
1325c8aa130bSThomas Abraham 		}
1326c8aa130bSThomas Abraham 	}
1327c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1328c8aa130bSThomas Abraham 
1329c8aa130bSThomas Abraham 	if (!genpd)
1330c8aa130bSThomas Abraham 		return -EINVAL;
1331c8aa130bSThomas Abraham 
1332c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1333c8aa130bSThomas Abraham }
1334c8aa130bSThomas Abraham 
1335c8aa130bSThomas Abraham /**
1336f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1337f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1338f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1339f721889fSRafael J. Wysocki  */
1340f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1341f721889fSRafael J. Wysocki 			   struct device *dev)
1342f721889fSRafael J. Wysocki {
13436ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
13444605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1345efa69025SRafael J. Wysocki 	int ret = 0;
1346f721889fSRafael J. Wysocki 
1347f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1348f721889fSRafael J. Wysocki 
1349efa69025SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
1350efa69025SRafael J. Wysocki 	    ||  IS_ERR_OR_NULL(dev->pm_domain)
1351efa69025SRafael J. Wysocki 	    ||  pd_to_genpd(dev->pm_domain) != genpd)
1352f721889fSRafael J. Wysocki 		return -EINVAL;
1353f721889fSRafael J. Wysocki 
135417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1355f721889fSRafael J. Wysocki 
1356596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1357596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1358596ba34bSRafael J. Wysocki 		goto out;
1359596ba34bSRafael J. Wysocki 	}
1360596ba34bSRafael J. Wysocki 
13616ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
13626ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
13636ff7bb0dSRafael J. Wysocki 
13646ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1365f721889fSRafael J. Wysocki 	dev->pm_domain = NULL;
1366efa69025SRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1367efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
1368efa69025SRafael J. Wysocki 	dev->power.subsys_data->domain_data = NULL;
13696ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1370f721889fSRafael J. Wysocki 
13716ff7bb0dSRafael J. Wysocki 	gpd_data = to_gpd_data(pdd);
13726ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
13736ff7bb0dSRafael J. Wysocki 	pdd->dev = NULL;
13746ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
13756ff7bb0dSRafael J. Wysocki 
13766ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
13776ff7bb0dSRafael J. Wysocki 
13786ff7bb0dSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
13796ff7bb0dSRafael J. Wysocki 	kfree(gpd_data);
13806ff7bb0dSRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
13816ff7bb0dSRafael J. Wysocki 	return 0;
1382f721889fSRafael J. Wysocki 
1383596ba34bSRafael J. Wysocki  out:
138417b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1385f721889fSRafael J. Wysocki 
1386f721889fSRafael J. Wysocki 	return ret;
1387f721889fSRafael J. Wysocki }
1388f721889fSRafael J. Wysocki 
1389f721889fSRafael J. Wysocki /**
13901e78a0c7SRafael J. Wysocki  * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device.
13911e78a0c7SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
13921e78a0c7SRafael J. Wysocki  * @val: The new value of the device's "always on" flag.
13931e78a0c7SRafael J. Wysocki  */
13941e78a0c7SRafael J. Wysocki void pm_genpd_dev_always_on(struct device *dev, bool val)
13951e78a0c7SRafael J. Wysocki {
13961e78a0c7SRafael J. Wysocki 	struct pm_subsys_data *psd;
13971e78a0c7SRafael J. Wysocki 	unsigned long flags;
13981e78a0c7SRafael J. Wysocki 
13991e78a0c7SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
14001e78a0c7SRafael J. Wysocki 
14011e78a0c7SRafael J. Wysocki 	psd = dev_to_psd(dev);
14021e78a0c7SRafael J. Wysocki 	if (psd && psd->domain_data)
14031e78a0c7SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->always_on = val;
14041e78a0c7SRafael J. Wysocki 
14051e78a0c7SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
14061e78a0c7SRafael J. Wysocki }
14071e78a0c7SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);
14081e78a0c7SRafael J. Wysocki 
14091e78a0c7SRafael J. Wysocki /**
1410ca1d72f0SRafael J. Wysocki  * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
1411ca1d72f0SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
1412ca1d72f0SRafael J. Wysocki  * @val: The new value of the device's "need restore" flag.
1413ca1d72f0SRafael J. Wysocki  */
1414ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val)
1415ca1d72f0SRafael J. Wysocki {
1416ca1d72f0SRafael J. Wysocki 	struct pm_subsys_data *psd;
1417ca1d72f0SRafael J. Wysocki 	unsigned long flags;
1418ca1d72f0SRafael J. Wysocki 
1419ca1d72f0SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
1420ca1d72f0SRafael J. Wysocki 
1421ca1d72f0SRafael J. Wysocki 	psd = dev_to_psd(dev);
1422ca1d72f0SRafael J. Wysocki 	if (psd && psd->domain_data)
1423ca1d72f0SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->need_restore = val;
1424ca1d72f0SRafael J. Wysocki 
1425ca1d72f0SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1426ca1d72f0SRafael J. Wysocki }
1427ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
1428ca1d72f0SRafael J. Wysocki 
1429ca1d72f0SRafael J. Wysocki /**
1430f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1431f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1432bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1433f721889fSRafael J. Wysocki  */
1434f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1435bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1436f721889fSRafael J. Wysocki {
14375063ce15SRafael J. Wysocki 	struct gpd_link *link;
1438f721889fSRafael J. Wysocki 	int ret = 0;
1439f721889fSRafael J. Wysocki 
1440bc0403ffSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1441f721889fSRafael J. Wysocki 		return -EINVAL;
1442f721889fSRafael J. Wysocki 
144317b75ecaSRafael J. Wysocki  start:
144417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1445bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1446f721889fSRafael J. Wysocki 
1447bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1448bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1449bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
145017b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
145117b75ecaSRafael J. Wysocki 		goto start;
145217b75ecaSRafael J. Wysocki 	}
145317b75ecaSRafael J. Wysocki 
145417b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1455bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1456f721889fSRafael J. Wysocki 		ret = -EINVAL;
1457f721889fSRafael J. Wysocki 		goto out;
1458f721889fSRafael J. Wysocki 	}
1459f721889fSRafael J. Wysocki 
14604fcac10dSHuang Ying 	list_for_each_entry(link, &genpd->master_links, master_node) {
1461bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1462f721889fSRafael J. Wysocki 			ret = -EINVAL;
1463f721889fSRafael J. Wysocki 			goto out;
1464f721889fSRafael J. Wysocki 		}
1465f721889fSRafael J. Wysocki 	}
1466f721889fSRafael J. Wysocki 
14675063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
14685063ce15SRafael J. Wysocki 	if (!link) {
14695063ce15SRafael J. Wysocki 		ret = -ENOMEM;
14705063ce15SRafael J. Wysocki 		goto out;
14715063ce15SRafael J. Wysocki 	}
14725063ce15SRafael J. Wysocki 	link->master = genpd;
14735063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1474bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1475bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1476bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1477c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1478f721889fSRafael J. Wysocki 
1479f721889fSRafael J. Wysocki  out:
1480bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
148117b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1482f721889fSRafael J. Wysocki 
1483f721889fSRafael J. Wysocki 	return ret;
1484f721889fSRafael J. Wysocki }
1485f721889fSRafael J. Wysocki 
1486f721889fSRafael J. Wysocki /**
1487f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1488f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
14895063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1490f721889fSRafael J. Wysocki  */
1491f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
14925063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1493f721889fSRafael J. Wysocki {
14945063ce15SRafael J. Wysocki 	struct gpd_link *link;
1495f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1496f721889fSRafael J. Wysocki 
14975063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1498f721889fSRafael J. Wysocki 		return -EINVAL;
1499f721889fSRafael J. Wysocki 
150017b75ecaSRafael J. Wysocki  start:
150117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1502f721889fSRafael J. Wysocki 
15035063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
15045063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1505f721889fSRafael J. Wysocki 			continue;
1506f721889fSRafael J. Wysocki 
1507f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1508f721889fSRafael J. Wysocki 
150917b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
151017b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
151117b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
151217b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
151317b75ecaSRafael J. Wysocki 			goto start;
151417b75ecaSRafael J. Wysocki 		}
151517b75ecaSRafael J. Wysocki 
15165063ce15SRafael J. Wysocki 		list_del(&link->master_node);
15175063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
15185063ce15SRafael J. Wysocki 		kfree(link);
151917b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1520f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1521f721889fSRafael J. Wysocki 
1522f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1523f721889fSRafael J. Wysocki 
1524f721889fSRafael J. Wysocki 		ret = 0;
1525f721889fSRafael J. Wysocki 		break;
1526f721889fSRafael J. Wysocki 	}
1527f721889fSRafael J. Wysocki 
152817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1529f721889fSRafael J. Wysocki 
1530f721889fSRafael J. Wysocki 	return ret;
1531f721889fSRafael J. Wysocki }
1532f721889fSRafael J. Wysocki 
1533f721889fSRafael J. Wysocki /**
1534d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1535d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1536d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1537b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
1538d5e4cbfeSRafael J. Wysocki  */
1539b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1540b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1541d5e4cbfeSRafael J. Wysocki {
1542d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1543d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1544d5e4cbfeSRafael J. Wysocki 
1545d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data && ops))
1546d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1547d5e4cbfeSRafael J. Wysocki 
1548d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1549d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1550d5e4cbfeSRafael J. Wysocki 
1551d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1552d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1553d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1554d5e4cbfeSRafael J. Wysocki 
1555d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = *ops;
1556b02c999aSRafael J. Wysocki 		if (td)
1557b02c999aSRafael J. Wysocki 			gpd_data->td = *td;
1558d5e4cbfeSRafael J. Wysocki 	} else {
1559d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1560d5e4cbfeSRafael J. Wysocki 	}
1561d5e4cbfeSRafael J. Wysocki 
1562d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1563d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1564d5e4cbfeSRafael J. Wysocki 
1565d5e4cbfeSRafael J. Wysocki 	return ret;
1566d5e4cbfeSRafael J. Wysocki }
1567d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1568d5e4cbfeSRafael J. Wysocki 
1569d5e4cbfeSRafael J. Wysocki /**
1570b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1571d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1572b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
1573d5e4cbfeSRafael J. Wysocki  */
1574b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1575d5e4cbfeSRafael J. Wysocki {
1576d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1577d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1578d5e4cbfeSRafael J. Wysocki 
1579d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1580d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1581d5e4cbfeSRafael J. Wysocki 
1582d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1583d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1584d5e4cbfeSRafael J. Wysocki 
1585d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1586d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1587d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1588d5e4cbfeSRafael J. Wysocki 
1589d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = (struct gpd_dev_ops){ 0 };
1590b02c999aSRafael J. Wysocki 		if (clear_td)
1591b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
1592d5e4cbfeSRafael J. Wysocki 	} else {
1593d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1594d5e4cbfeSRafael J. Wysocki 	}
1595d5e4cbfeSRafael J. Wysocki 
1596d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1597d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1598d5e4cbfeSRafael J. Wysocki 
1599d5e4cbfeSRafael J. Wysocki 	return ret;
1600d5e4cbfeSRafael J. Wysocki }
1601b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1602d5e4cbfeSRafael J. Wysocki 
1603d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1604d23b9b00SRafael J. Wysocki 
1605d5e4cbfeSRafael J. Wysocki /**
1606ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1607ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1608ecf00475SRafael J. Wysocki  */
1609ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1610ecf00475SRafael J. Wysocki {
1611ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1612ecf00475SRafael J. Wysocki 
1613ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1614ecf00475SRafael J. Wysocki 	if (cb)
1615ecf00475SRafael J. Wysocki 		return cb(dev);
1616ecf00475SRafael J. Wysocki 
16170b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
16180b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_suspend;
16190b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
16200b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_suspend;
16210b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
16220b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_suspend;
16230b589741SRafael J. Wysocki 	else
16240b589741SRafael J. Wysocki 		cb = NULL;
1625ecf00475SRafael J. Wysocki 
16260b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
16270b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_suspend;
16280b589741SRafael J. Wysocki 
16290b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1630ecf00475SRafael J. Wysocki }
1631ecf00475SRafael J. Wysocki 
1632ecf00475SRafael J. Wysocki /**
1633ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1634ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1635ecf00475SRafael J. Wysocki  */
1636ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1637ecf00475SRafael J. Wysocki {
1638ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1639ecf00475SRafael J. Wysocki 
1640ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1641ecf00475SRafael J. Wysocki 	if (cb)
1642ecf00475SRafael J. Wysocki 		return cb(dev);
1643ecf00475SRafael J. Wysocki 
16440b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
16450b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_resume;
16460b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
16470b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_resume;
16480b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
16490b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_resume;
16500b589741SRafael J. Wysocki 	else
16510b589741SRafael J. Wysocki 		cb = NULL;
1652ecf00475SRafael J. Wysocki 
16530b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
16540b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_resume;
16550b589741SRafael J. Wysocki 
16560b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1657ecf00475SRafael J. Wysocki }
1658ecf00475SRafael J. Wysocki 
16590f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
16600f1d6986SRafael J. Wysocki 
1661ecf00475SRafael J. Wysocki /**
1662d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
1663d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1664d23b9b00SRafael J. Wysocki  */
1665d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
1666d23b9b00SRafael J. Wysocki {
1667c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
1668d23b9b00SRafael J. Wysocki 
1669d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
1670d23b9b00SRafael J. Wysocki }
1671d23b9b00SRafael J. Wysocki 
1672d23b9b00SRafael J. Wysocki /**
1673d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
1674d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1675d23b9b00SRafael J. Wysocki  */
1676d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
1677d23b9b00SRafael J. Wysocki {
1678c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
1679d23b9b00SRafael J. Wysocki 
16800496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_late(dev);
1681d23b9b00SRafael J. Wysocki }
1682d23b9b00SRafael J. Wysocki 
1683d23b9b00SRafael J. Wysocki /**
1684d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
1685d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1686d23b9b00SRafael J. Wysocki  */
1687d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
1688d23b9b00SRafael J. Wysocki {
1689c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
1690d23b9b00SRafael J. Wysocki 
16910496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_early(dev);
1692d23b9b00SRafael J. Wysocki }
1693d23b9b00SRafael J. Wysocki 
1694d23b9b00SRafael J. Wysocki /**
1695d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
1696d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1697d23b9b00SRafael J. Wysocki  */
1698d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
1699d23b9b00SRafael J. Wysocki {
1700c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
1701d23b9b00SRafael J. Wysocki 
1702d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
1703d23b9b00SRafael J. Wysocki }
1704d23b9b00SRafael J. Wysocki 
1705d23b9b00SRafael J. Wysocki /**
1706d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
1707d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1708d23b9b00SRafael J. Wysocki  */
1709d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
1710d23b9b00SRafael J. Wysocki {
1711d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
1712d23b9b00SRafael J. Wysocki 
1713d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
1714d23b9b00SRafael J. Wysocki }
1715d23b9b00SRafael J. Wysocki 
1716d23b9b00SRafael J. Wysocki /**
1717d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
1718d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1719d23b9b00SRafael J. Wysocki  */
1720d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
1721d23b9b00SRafael J. Wysocki {
1722d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
1723d23b9b00SRafael J. Wysocki 
17240496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_late(dev);
1725d23b9b00SRafael J. Wysocki }
1726d23b9b00SRafael J. Wysocki 
1727d23b9b00SRafael J. Wysocki /**
1728d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
1729d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1730d23b9b00SRafael J. Wysocki  */
1731d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
1732d23b9b00SRafael J. Wysocki {
1733d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
1734d23b9b00SRafael J. Wysocki 
17350496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_early(dev);
1736d23b9b00SRafael J. Wysocki }
1737d23b9b00SRafael J. Wysocki 
1738d23b9b00SRafael J. Wysocki /**
1739d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
1740d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1741d23b9b00SRafael J. Wysocki  */
1742d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
1743d23b9b00SRafael J. Wysocki {
1744d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
1745d23b9b00SRafael J. Wysocki 
1746d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
1747d23b9b00SRafael J. Wysocki }
1748d23b9b00SRafael J. Wysocki 
17490f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
17500f1d6986SRafael J. Wysocki 
17510f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
17520f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
17530f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
17540f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
17550f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
17560f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
17570f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
17580f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
17590f1d6986SRafael J. Wysocki 
17600f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
17610f1d6986SRafael J. Wysocki 
1762d23b9b00SRafael J. Wysocki /**
1763f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1764f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1765f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1766f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1767f721889fSRafael J. Wysocki  */
1768f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1769f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1770f721889fSRafael J. Wysocki {
1771f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1772f721889fSRafael J. Wysocki 		return;
1773f721889fSRafael J. Wysocki 
17745063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
17755063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
1776f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1777f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1778f721889fSRafael J. Wysocki 	genpd->gov = gov;
1779f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1780f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1781c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
178217b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
178317b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1784c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1785c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1786596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1787221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
17886ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1789f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1790f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1791f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1792596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1793596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
17940496c8aeSRafael J. Wysocki 	genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
1795596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1796596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
17970496c8aeSRafael J. Wysocki 	genpd->domain.ops.resume_early = pm_genpd_resume_early;
1798596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1799596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
18000496c8aeSRafael J. Wysocki 	genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
1801596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1802596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
18030496c8aeSRafael J. Wysocki 	genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
1804596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1805d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
18060496c8aeSRafael J. Wysocki 	genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
1807d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
1808596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
18090496c8aeSRafael J. Wysocki 	genpd->domain.ops.restore_early = pm_genpd_resume_early;
1810d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
1811596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
1812ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
1813ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
1814c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
1815c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
1816c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
1817c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
1818d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
1819d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
1820d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
1821d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
18225125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
18235125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
18245125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
18255125bbf3SRafael J. Wysocki }
1826