xref: /openbmc/linux/drivers/base/power/domain.c (revision 0b589741)
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;
342f721889fSRafael J. Wysocki 
343cd0ea672SRafael J. Wysocki 	if (!gpd_data->need_restore)
344f721889fSRafael J. Wysocki 		return;
345f721889fSRafael J. Wysocki 
34617b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
34717b75ecaSRafael J. Wysocki 
348d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
349ecf00475SRafael J. Wysocki 	genpd_restore_dev(genpd, dev);
350d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
351f721889fSRafael J. Wysocki 
35217b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
35317b75ecaSRafael J. Wysocki 
354cd0ea672SRafael J. Wysocki 	gpd_data->need_restore = false;
355f721889fSRafael J. Wysocki }
356f721889fSRafael J. Wysocki 
357f721889fSRafael J. Wysocki /**
358c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
359c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
360c6d22b37SRafael J. Wysocki  *
361c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
362c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
363c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
364c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
365c6d22b37SRafael J. Wysocki  */
366c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
367c6d22b37SRafael J. Wysocki {
36817877eb5SRafael J. Wysocki 	return genpd->status == GPD_STATE_WAIT_MASTER
3693f241775SRafael J. Wysocki 		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
370c6d22b37SRafael J. Wysocki }
371c6d22b37SRafael J. Wysocki 
372c6d22b37SRafael J. Wysocki /**
37356375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
37456375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
37556375fd4SRafael J. Wysocki  *
37656375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
37756375fd4SRafael J. Wysocki  * before.
37856375fd4SRafael J. Wysocki  */
3790bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
38056375fd4SRafael J. Wysocki {
38156375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
38256375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
38356375fd4SRafael J. Wysocki }
38456375fd4SRafael J. Wysocki 
38556375fd4SRafael J. Wysocki /**
386f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
387f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
388f721889fSRafael J. Wysocki  *
389f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
390f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
391f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
392f721889fSRafael J. Wysocki  */
393f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
39417b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
395f721889fSRafael J. Wysocki {
3964605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
3975063ce15SRafael J. Wysocki 	struct gpd_link *link;
398f721889fSRafael J. Wysocki 	unsigned int not_suspended;
399c6d22b37SRafael J. Wysocki 	int ret = 0;
400f721889fSRafael J. Wysocki 
401c6d22b37SRafael J. Wysocki  start:
402c6d22b37SRafael J. Wysocki 	/*
403c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
404c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
4055063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
406c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
4073f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
408c6d22b37SRafael J. Wysocki 	 */
4093f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
41017877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
4113f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
412f721889fSRafael J. Wysocki 		return 0;
413f721889fSRafael J. Wysocki 
414c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
415f721889fSRafael J. Wysocki 		return -EBUSY;
416f721889fSRafael J. Wysocki 
417f721889fSRafael J. Wysocki 	not_suspended = 0;
4184605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
4190aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
4201e78a0c7SRafael J. Wysocki 		    || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on))
421f721889fSRafael J. Wysocki 			not_suspended++;
422f721889fSRafael J. Wysocki 
423f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
424f721889fSRafael J. Wysocki 		return -EBUSY;
425f721889fSRafael J. Wysocki 
426c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
427c6d22b37SRafael J. Wysocki 		/*
428c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
429c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
430c6d22b37SRafael J. Wysocki 		 */
431c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
432c6d22b37SRafael J. Wysocki 		return 0;
433c6d22b37SRafael J. Wysocki 	}
434c6d22b37SRafael J. Wysocki 
435f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
436f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
437f721889fSRafael J. Wysocki 			return -EAGAIN;
438f721889fSRafael J. Wysocki 	}
439f721889fSRafael J. Wysocki 
44017b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
441c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
44217b75ecaSRafael J. Wysocki 
4434605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
4443c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
4454605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
4463f241775SRafael J. Wysocki 
4473f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
4483f241775SRafael J. Wysocki 			goto out;
4493f241775SRafael J. Wysocki 
450697a7f37SRafael J. Wysocki 		if (ret) {
451697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
452697a7f37SRafael J. Wysocki 			goto out;
453697a7f37SRafael J. Wysocki 		}
454f721889fSRafael J. Wysocki 
455c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
456c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
457c6d22b37SRafael J. Wysocki 			goto start;
458c6d22b37SRafael J. Wysocki 		}
459c6d22b37SRafael J. Wysocki 	}
46017b75ecaSRafael J. Wysocki 
4613c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
4620140d8bdSRafael J. Wysocki 		ktime_t time_start;
4630140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
4640140d8bdSRafael J. Wysocki 
4653c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
4663c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
467c6d22b37SRafael J. Wysocki 			goto out;
468c6d22b37SRafael J. Wysocki 		}
46917b75ecaSRafael J. Wysocki 
4700140d8bdSRafael J. Wysocki 		time_start = ktime_get();
4710140d8bdSRafael J. Wysocki 
4723c07cbc4SRafael J. Wysocki 		/*
4735063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
4745063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
4753c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
4763c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
4773c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
4783c07cbc4SRafael J. Wysocki 		 * happen very often).
4793c07cbc4SRafael J. Wysocki 		 */
480d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
481d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
482d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
483d2805402SRafael J. Wysocki 			goto out;
484d2805402SRafael J. Wysocki 		}
4850140d8bdSRafael J. Wysocki 
4860140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
487e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_off_latency_ns) {
4880140d8bdSRafael J. Wysocki 			genpd->power_off_latency_ns = elapsed_ns;
4896ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
490e84b2c20SRafael J. Wysocki 			if (genpd->name)
491e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-off latency exceeded, "
492e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
493e84b2c20SRafael J. Wysocki 					elapsed_ns);
494e84b2c20SRafael J. Wysocki 		}
495d2805402SRafael J. Wysocki 	}
496f721889fSRafael J. Wysocki 
49717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
498221e9b58SRafael J. Wysocki 
4995063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
5005063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
5015063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
5025063ce15SRafael J. Wysocki 	}
50317b75ecaSRafael J. Wysocki 
504c6d22b37SRafael J. Wysocki  out:
505c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
506c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
507c6d22b37SRafael J. Wysocki 	return ret;
508f721889fSRafael J. Wysocki }
509f721889fSRafael J. Wysocki 
510f721889fSRafael J. Wysocki /**
511f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
512f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
513f721889fSRafael J. Wysocki  */
514f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
515f721889fSRafael J. Wysocki {
516f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
517f721889fSRafael J. Wysocki 
518f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
519f721889fSRafael J. Wysocki 
52017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
521f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
52217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
523f721889fSRafael J. Wysocki }
524f721889fSRafael J. Wysocki 
525f721889fSRafael J. Wysocki /**
526f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
527f721889fSRafael J. Wysocki  * @dev: Device to suspend.
528f721889fSRafael J. Wysocki  *
529f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
530f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
531f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
532f721889fSRafael J. Wysocki  */
533f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
534f721889fSRafael J. Wysocki {
535f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
536b02c999aSRafael J. Wysocki 	bool (*stop_ok)(struct device *__dev);
537d5e4cbfeSRafael J. Wysocki 	int ret;
538f721889fSRafael J. Wysocki 
539f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
540f721889fSRafael J. Wysocki 
5415248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5425248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
543f721889fSRafael J. Wysocki 		return -EINVAL;
544f721889fSRafael J. Wysocki 
5450aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5460aa2a221SRafael J. Wysocki 
5471e78a0c7SRafael J. Wysocki 	if (dev_gpd_data(dev)->always_on)
5481e78a0c7SRafael J. Wysocki 		return -EBUSY;
5491e78a0c7SRafael J. Wysocki 
550b02c999aSRafael J. Wysocki 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
551b02c999aSRafael J. Wysocki 	if (stop_ok && !stop_ok(dev))
552b02c999aSRafael J. Wysocki 		return -EBUSY;
553b02c999aSRafael J. Wysocki 
554d5e4cbfeSRafael J. Wysocki 	ret = genpd_stop_dev(genpd, dev);
555f721889fSRafael J. Wysocki 	if (ret)
55617b75ecaSRafael J. Wysocki 		return ret;
55717b75ecaSRafael J. Wysocki 
5580aa2a221SRafael J. Wysocki 	/*
5590aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
5600aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
5610aa2a221SRafael J. Wysocki 	 */
5620aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
5630aa2a221SRafael J. Wysocki 		return 0;
5640aa2a221SRafael J. Wysocki 
565c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
566f721889fSRafael J. Wysocki 	genpd->in_progress++;
567f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
568f721889fSRafael J. Wysocki 	genpd->in_progress--;
569c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
570f721889fSRafael J. Wysocki 
571f721889fSRafael J. Wysocki 	return 0;
572f721889fSRafael J. Wysocki }
573f721889fSRafael J. Wysocki 
574f721889fSRafael J. Wysocki /**
575f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
576f721889fSRafael J. Wysocki  * @dev: Device to resume.
577f721889fSRafael J. Wysocki  *
578f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
579f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
580f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
581f721889fSRafael J. Wysocki  */
582f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
583f721889fSRafael J. Wysocki {
584f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
585c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
586f721889fSRafael J. Wysocki 	int ret;
587f721889fSRafael J. Wysocki 
588f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
589f721889fSRafael J. Wysocki 
5905248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5915248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
592f721889fSRafael J. Wysocki 		return -EINVAL;
593f721889fSRafael J. Wysocki 
5940aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5950aa2a221SRafael J. Wysocki 
5960aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
5970aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
5980aa2a221SRafael J. Wysocki 		goto out;
5990aa2a221SRafael J. Wysocki 
600c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
6013f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
6023f241775SRafael J. Wysocki 	if (ret) {
6033f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
6043f241775SRafael J. Wysocki 		return ret;
6053f241775SRafael J. Wysocki 	}
60617b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
607c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
608c6d22b37SRafael J. Wysocki 	for (;;) {
609c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
610c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
611c6d22b37SRafael J. Wysocki 		/*
612c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
613c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
614c6d22b37SRafael J. Wysocki 		 * not wait.
615c6d22b37SRafael J. Wysocki 		 */
616c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
617c6d22b37SRafael J. Wysocki 			break;
618c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
619c6d22b37SRafael J. Wysocki 
620c6d22b37SRafael J. Wysocki 		schedule();
621c6d22b37SRafael J. Wysocki 
622c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
623c6d22b37SRafael J. Wysocki 	}
624c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
625cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
626c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
627c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
62817b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
629c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
63017b75ecaSRafael J. Wysocki 
6310aa2a221SRafael J. Wysocki  out:
632d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
633f721889fSRafael J. Wysocki 
634f721889fSRafael J. Wysocki 	return 0;
635f721889fSRafael J. Wysocki }
636f721889fSRafael J. Wysocki 
63717f2ae7fSRafael J. Wysocki /**
63817f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
63917f2ae7fSRafael J. Wysocki  */
64017f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
64117f2ae7fSRafael J. Wysocki {
64217f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
64317f2ae7fSRafael J. Wysocki 
64417f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
64517f2ae7fSRafael J. Wysocki 
64617f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
64717f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
64817f2ae7fSRafael J. Wysocki 
64917f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
65017f2ae7fSRafael J. Wysocki }
65117f2ae7fSRafael J. Wysocki 
652f721889fSRafael J. Wysocki #else
653f721889fSRafael J. Wysocki 
6546ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
6556ff7bb0dSRafael J. Wysocki 					    unsigned long val, void *ptr)
6566ff7bb0dSRafael J. Wysocki {
6576ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
6586ff7bb0dSRafael J. Wysocki }
6596ff7bb0dSRafael J. Wysocki 
660f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
661f721889fSRafael J. Wysocki 
662f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
663f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
664f721889fSRafael J. Wysocki 
665f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
666f721889fSRafael J. Wysocki 
667596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
668596ba34bSRafael J. Wysocki 
669d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
670d5e4cbfeSRafael J. Wysocki 				    struct device *dev)
671d5e4cbfeSRafael J. Wysocki {
672d5e4cbfeSRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
673d5e4cbfeSRafael J. Wysocki }
674d5e4cbfeSRafael J. Wysocki 
675d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
676d23b9b00SRafael J. Wysocki {
677d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
678d23b9b00SRafael J. Wysocki }
679d23b9b00SRafael J. Wysocki 
680d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
681d23b9b00SRafael J. Wysocki {
682d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
683d23b9b00SRafael J. Wysocki }
684d23b9b00SRafael J. Wysocki 
685d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
686d23b9b00SRafael J. Wysocki {
687d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
688d23b9b00SRafael J. Wysocki }
689d23b9b00SRafael J. Wysocki 
690d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
691d23b9b00SRafael J. Wysocki {
692d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
693d23b9b00SRafael J. Wysocki }
694d23b9b00SRafael J. Wysocki 
695d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
696d23b9b00SRafael J. Wysocki {
697d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
698d23b9b00SRafael J. Wysocki }
699d23b9b00SRafael J. Wysocki 
700d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
701d23b9b00SRafael J. Wysocki {
702d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
703d23b9b00SRafael J. Wysocki }
704d23b9b00SRafael J. Wysocki 
705d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
706d23b9b00SRafael J. Wysocki {
707d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
708d23b9b00SRafael J. Wysocki }
709d23b9b00SRafael J. Wysocki 
710d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
711d23b9b00SRafael J. Wysocki {
712d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
713d23b9b00SRafael J. Wysocki }
714d23b9b00SRafael J. Wysocki 
715596ba34bSRafael J. Wysocki /**
7165063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
717596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
718596ba34bSRafael J. Wysocki  *
719596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
7205063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
721596ba34bSRafael J. Wysocki  *
722596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
723596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
724596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
725596ba34bSRafael J. Wysocki  */
726596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
727596ba34bSRafael J. Wysocki {
7285063ce15SRafael J. Wysocki 	struct gpd_link *link;
729596ba34bSRafael J. Wysocki 
73017b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
731596ba34bSRafael J. Wysocki 		return;
732596ba34bSRafael J. Wysocki 
733c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
734c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
735596ba34bSRafael J. Wysocki 		return;
736596ba34bSRafael J. Wysocki 
737596ba34bSRafael J. Wysocki 	if (genpd->power_off)
738596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
739596ba34bSRafael J. Wysocki 
74017b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
7415063ce15SRafael J. Wysocki 
7425063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
7435063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
7445063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
745596ba34bSRafael J. Wysocki 	}
746596ba34bSRafael J. Wysocki }
747596ba34bSRafael J. Wysocki 
748596ba34bSRafael J. Wysocki /**
7494ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
7504ecd6e65SRafael J. Wysocki  * @dev: Device to check.
7514ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
7524ecd6e65SRafael J. Wysocki  *
7534ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
7544ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
7554ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
7564ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
7574ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
7584ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
7594ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
7604ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
7614ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
7624ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
7634ecd6e65SRafael J. Wysocki  */
7644ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
7654ecd6e65SRafael J. Wysocki {
7664ecd6e65SRafael J. Wysocki 	bool active_wakeup;
7674ecd6e65SRafael J. Wysocki 
7684ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
7694ecd6e65SRafael J. Wysocki 		return false;
7704ecd6e65SRafael J. Wysocki 
771d5e4cbfeSRafael J. Wysocki 	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
7724ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
7734ecd6e65SRafael J. Wysocki }
7744ecd6e65SRafael J. Wysocki 
7754ecd6e65SRafael J. Wysocki /**
776596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
777596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
778596ba34bSRafael J. Wysocki  *
779596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
780596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
781596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
782596ba34bSRafael J. Wysocki  * consisting of I/O devices.
783596ba34bSRafael J. Wysocki  */
784596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
785596ba34bSRafael J. Wysocki {
786596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
787b6c10c84SRafael J. Wysocki 	int ret;
788596ba34bSRafael J. Wysocki 
789596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
790596ba34bSRafael J. Wysocki 
791596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
792596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
793596ba34bSRafael J. Wysocki 		return -EINVAL;
794596ba34bSRafael J. Wysocki 
79517b75ecaSRafael J. Wysocki 	/*
79617b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
79717b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
79817b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
79917b75ecaSRafael J. Wysocki 	 */
80017b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
80117b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
80217b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
80317b75ecaSRafael J. Wysocki 
80417b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
80517b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
80617b75ecaSRafael J. Wysocki 		return -EBUSY;
80717b75ecaSRafael J. Wysocki 	}
80817b75ecaSRafael J. Wysocki 
8094ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
8104ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
8114ecd6e65SRafael J. Wysocki 
81217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
813596ba34bSRafael J. Wysocki 
81465533bbfSRafael J. Wysocki 	if (genpd->prepared_count++ == 0) {
81565533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
81617b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
81765533bbfSRafael J. Wysocki 	}
81817b75ecaSRafael J. Wysocki 
81917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
820596ba34bSRafael J. Wysocki 
821596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
82217b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
823596ba34bSRafael J. Wysocki 		return 0;
824596ba34bSRafael J. Wysocki 	}
825596ba34bSRafael J. Wysocki 
826596ba34bSRafael J. Wysocki 	/*
82717b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
82817b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
829d5e4cbfeSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
83017b75ecaSRafael J. Wysocki 	 * to make it operational.
831596ba34bSRafael J. Wysocki 	 */
83217b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
833596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
834596ba34bSRafael J. Wysocki 
835b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
836b6c10c84SRafael J. Wysocki 	if (ret) {
837b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
838b6c10c84SRafael J. Wysocki 
839b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
840b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
841b6c10c84SRafael J. Wysocki 
842b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
84317b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
844b6c10c84SRafael J. Wysocki 	}
84517b75ecaSRafael J. Wysocki 
84617b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
847b6c10c84SRafael J. Wysocki 	return ret;
848596ba34bSRafael J. Wysocki }
849596ba34bSRafael J. Wysocki 
850596ba34bSRafael J. Wysocki /**
851596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
852596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
853596ba34bSRafael J. Wysocki  *
854596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
855596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
856596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
857596ba34bSRafael J. Wysocki  */
858596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
859596ba34bSRafael J. Wysocki {
860596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
861596ba34bSRafael J. Wysocki 
862596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
863596ba34bSRafael J. Wysocki 
864596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
865596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
866596ba34bSRafael J. Wysocki 		return -EINVAL;
867596ba34bSRafael J. Wysocki 
868d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
869596ba34bSRafael J. Wysocki }
870596ba34bSRafael J. Wysocki 
871596ba34bSRafael J. Wysocki /**
8720496c8aeSRafael J. Wysocki  * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
873596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
874596ba34bSRafael J. Wysocki  *
875596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
876596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
877596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
878596ba34bSRafael J. Wysocki  */
8790496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev)
880596ba34bSRafael J. Wysocki {
881596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
882596ba34bSRafael J. Wysocki 
883596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
884596ba34bSRafael J. Wysocki 
885596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
886596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
887596ba34bSRafael J. Wysocki 		return -EINVAL;
888596ba34bSRafael J. Wysocki 
8890496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
8900496c8aeSRafael J. Wysocki }
891596ba34bSRafael J. Wysocki 
8920496c8aeSRafael J. Wysocki /**
8930496c8aeSRafael J. Wysocki  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
8940496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
8950496c8aeSRafael J. Wysocki  *
8960496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
8970496c8aeSRafael J. Wysocki  * been stopped.
8980496c8aeSRafael J. Wysocki  */
8990496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
9000496c8aeSRafael J. Wysocki {
9010496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
902596ba34bSRafael J. Wysocki 
9030496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
9040496c8aeSRafael J. Wysocki 
9050496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9060496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9070496c8aeSRafael J. Wysocki 		return -EINVAL;
9080496c8aeSRafael J. Wysocki 
9091e78a0c7SRafael J. Wysocki 	if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
9100496c8aeSRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
911d4f2d87aSRafael J. Wysocki 		return 0;
912d4f2d87aSRafael J. Wysocki 
913d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
914596ba34bSRafael J. Wysocki 
915596ba34bSRafael J. Wysocki 	/*
916596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
917596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
918596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
919596ba34bSRafael J. Wysocki 	 */
920596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
921596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
922596ba34bSRafael J. Wysocki 
923596ba34bSRafael J. Wysocki 	return 0;
924596ba34bSRafael J. Wysocki }
925596ba34bSRafael J. Wysocki 
926596ba34bSRafael J. Wysocki /**
9270496c8aeSRafael J. Wysocki  * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
928596ba34bSRafael J. Wysocki  * @dev: Device to resume.
929596ba34bSRafael J. Wysocki  *
9300496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
931596ba34bSRafael J. Wysocki  */
932596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
933596ba34bSRafael J. Wysocki {
934596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
935596ba34bSRafael J. Wysocki 
936596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
937596ba34bSRafael J. Wysocki 
938596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
939596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
940596ba34bSRafael J. Wysocki 		return -EINVAL;
941596ba34bSRafael J. Wysocki 
9421e78a0c7SRafael J. Wysocki 	if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
943cc85b207SRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
944596ba34bSRafael J. Wysocki 		return 0;
945596ba34bSRafael J. Wysocki 
946596ba34bSRafael J. Wysocki 	/*
947596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
948596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
949596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
950596ba34bSRafael J. Wysocki 	 */
951596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
952596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
953596ba34bSRafael J. Wysocki 
9540496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
955596ba34bSRafael J. Wysocki }
956596ba34bSRafael J. Wysocki 
957596ba34bSRafael J. Wysocki /**
9580496c8aeSRafael J. Wysocki  * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
9590496c8aeSRafael J. Wysocki  * @dev: Device to resume.
9600496c8aeSRafael J. Wysocki  *
9610496c8aeSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
9620496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
9630496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
9640496c8aeSRafael J. Wysocki  * devices.
9650496c8aeSRafael J. Wysocki  */
9660496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev)
9670496c8aeSRafael J. Wysocki {
9680496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
9690496c8aeSRafael J. Wysocki 
9700496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
9710496c8aeSRafael J. Wysocki 
9720496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9730496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9740496c8aeSRafael J. Wysocki 		return -EINVAL;
9750496c8aeSRafael J. Wysocki 
9760496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
9770496c8aeSRafael J. Wysocki }
9780496c8aeSRafael J. Wysocki 
9790496c8aeSRafael J. Wysocki /**
9800496c8aeSRafael J. Wysocki  * pm_genpd_resume - Resume of device in an I/O PM domain.
981596ba34bSRafael J. Wysocki  * @dev: Device to resume.
982596ba34bSRafael J. Wysocki  *
983596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
984596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
985596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
986596ba34bSRafael J. Wysocki  */
987596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
988596ba34bSRafael J. Wysocki {
989596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
990596ba34bSRafael J. Wysocki 
991596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
992596ba34bSRafael J. Wysocki 
993596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
994596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
995596ba34bSRafael J. Wysocki 		return -EINVAL;
996596ba34bSRafael J. Wysocki 
997d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
998596ba34bSRafael J. Wysocki }
999596ba34bSRafael J. Wysocki 
1000596ba34bSRafael J. Wysocki /**
10010496c8aeSRafael J. Wysocki  * pm_genpd_freeze - Freezing a device in an I/O PM domain.
1002596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1003596ba34bSRafael J. Wysocki  *
1004596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
1005596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1006596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1007596ba34bSRafael J. Wysocki  */
1008596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
1009596ba34bSRafael J. Wysocki {
1010596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1011596ba34bSRafael J. Wysocki 
1012596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1013596ba34bSRafael J. Wysocki 
1014596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1015596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1016596ba34bSRafael J. Wysocki 		return -EINVAL;
1017596ba34bSRafael J. Wysocki 
1018d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
1019596ba34bSRafael J. Wysocki }
1020596ba34bSRafael J. Wysocki 
1021596ba34bSRafael J. Wysocki /**
10220496c8aeSRafael J. Wysocki  * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
10230496c8aeSRafael J. Wysocki  * @dev: Device to freeze.
10240496c8aeSRafael J. Wysocki  *
10250496c8aeSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
10260496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10270496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10280496c8aeSRafael J. Wysocki  * devices.
10290496c8aeSRafael J. Wysocki  */
10300496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev)
10310496c8aeSRafael J. Wysocki {
10320496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
10330496c8aeSRafael J. Wysocki 
10340496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10350496c8aeSRafael J. Wysocki 
10360496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10370496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10380496c8aeSRafael J. Wysocki 		return -EINVAL;
10390496c8aeSRafael J. Wysocki 
10400496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
10410496c8aeSRafael J. Wysocki }
10420496c8aeSRafael J. Wysocki 
10430496c8aeSRafael J. Wysocki /**
10440496c8aeSRafael J. Wysocki  * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1045596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1046596ba34bSRafael J. Wysocki  *
1047596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1048596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1049596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1050596ba34bSRafael J. Wysocki  * devices.
1051596ba34bSRafael J. Wysocki  */
1052596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
1053596ba34bSRafael J. Wysocki {
1054596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1055596ba34bSRafael J. Wysocki 
1056596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1057596ba34bSRafael J. Wysocki 
1058596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1059596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1060596ba34bSRafael J. Wysocki 		return -EINVAL;
1061596ba34bSRafael J. Wysocki 
10621e78a0c7SRafael J. Wysocki 	return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
10631e78a0c7SRafael J. Wysocki 		0 : genpd_stop_dev(genpd, dev);
1064596ba34bSRafael J. Wysocki }
1065596ba34bSRafael J. Wysocki 
1066596ba34bSRafael J. Wysocki /**
10670496c8aeSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1068596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1069596ba34bSRafael J. Wysocki  *
10700496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
10710496c8aeSRafael J. Wysocki  * before the system transition.
1072596ba34bSRafael J. Wysocki  */
1073596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
1074596ba34bSRafael J. Wysocki {
1075596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1076596ba34bSRafael J. Wysocki 
1077596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1078596ba34bSRafael J. Wysocki 
1079596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1080596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1081596ba34bSRafael J. Wysocki 		return -EINVAL;
1082596ba34bSRafael J. Wysocki 
10831e78a0c7SRafael J. Wysocki 	return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
10841e78a0c7SRafael J. Wysocki 		0 : genpd_start_dev(genpd, dev);
10850496c8aeSRafael J. Wysocki }
1086596ba34bSRafael J. Wysocki 
10870496c8aeSRafael J. Wysocki /**
10880496c8aeSRafael J. Wysocki  * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
10890496c8aeSRafael J. Wysocki  * @dev: Device to thaw.
10900496c8aeSRafael J. Wysocki  *
10910496c8aeSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
10920496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10930496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10940496c8aeSRafael J. Wysocki  * devices.
10950496c8aeSRafael J. Wysocki  */
10960496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev)
10970496c8aeSRafael J. Wysocki {
10980496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1099596ba34bSRafael J. Wysocki 
11000496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
11010496c8aeSRafael J. Wysocki 
11020496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
11030496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
11040496c8aeSRafael J. Wysocki 		return -EINVAL;
11050496c8aeSRafael J. Wysocki 
11060496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
1107596ba34bSRafael J. Wysocki }
1108596ba34bSRafael J. Wysocki 
1109596ba34bSRafael J. Wysocki /**
1110596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1111596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1112596ba34bSRafael J. Wysocki  *
1113596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1114596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1115596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1116596ba34bSRafael J. Wysocki  */
1117596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1118596ba34bSRafael J. Wysocki {
1119596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1120596ba34bSRafael J. Wysocki 
1121596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1122596ba34bSRafael J. Wysocki 
1123596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1124596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1125596ba34bSRafael J. Wysocki 		return -EINVAL;
1126596ba34bSRafael J. Wysocki 
1127d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1128596ba34bSRafael J. Wysocki }
1129596ba34bSRafael J. Wysocki 
1130596ba34bSRafael J. Wysocki /**
11310496c8aeSRafael J. Wysocki  * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1132596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1133596ba34bSRafael J. Wysocki  *
11340496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
11350496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1136596ba34bSRafael J. Wysocki  */
1137596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1138596ba34bSRafael J. Wysocki {
1139596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1140596ba34bSRafael J. Wysocki 
1141596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1142596ba34bSRafael J. Wysocki 
1143596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1144596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1145596ba34bSRafael J. Wysocki 		return -EINVAL;
1146596ba34bSRafael J. Wysocki 
1147596ba34bSRafael J. Wysocki 	/*
1148596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1149596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1150596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
115165533bbfSRafael J. Wysocki 	 *
115265533bbfSRafael J. Wysocki 	 * At this point suspended_count == 0 means we are being run for the
115365533bbfSRafael J. Wysocki 	 * first time for the given domain in the present cycle.
115465533bbfSRafael J. Wysocki 	 */
115565533bbfSRafael J. Wysocki 	if (genpd->suspended_count++ == 0) {
115665533bbfSRafael J. Wysocki 		/*
115765533bbfSRafael J. Wysocki 		 * The boot kernel might put the domain into arbitrary state,
115865533bbfSRafael J. Wysocki 		 * so make it appear as powered off to pm_genpd_poweron(), so
115965533bbfSRafael J. Wysocki 		 * that it tries to power it on in case it was really off.
1160596ba34bSRafael J. Wysocki 		 */
116117b75ecaSRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1162596ba34bSRafael J. Wysocki 		if (genpd->suspend_power_off) {
1163596ba34bSRafael J. Wysocki 			/*
116465533bbfSRafael J. Wysocki 			 * If the domain was off before the hibernation, make
116565533bbfSRafael J. Wysocki 			 * sure it will be off going forward.
1166596ba34bSRafael J. Wysocki 			 */
1167596ba34bSRafael J. Wysocki 			if (genpd->power_off)
1168596ba34bSRafael J. Wysocki 				genpd->power_off(genpd);
116965533bbfSRafael J. Wysocki 
1170596ba34bSRafael J. Wysocki 			return 0;
1171596ba34bSRafael J. Wysocki 		}
117265533bbfSRafael J. Wysocki 	}
1173596ba34bSRafael J. Wysocki 
117418dd2eceSRafael J. Wysocki 	if (genpd->suspend_power_off)
117518dd2eceSRafael J. Wysocki 		return 0;
117618dd2eceSRafael J. Wysocki 
1177596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
1178596ba34bSRafael J. Wysocki 
11791e78a0c7SRafael J. Wysocki 	return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev);
1180596ba34bSRafael J. Wysocki }
1181596ba34bSRafael J. Wysocki 
1182596ba34bSRafael J. Wysocki /**
1183596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1184596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1185596ba34bSRafael J. Wysocki  *
1186596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1187596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1188596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1189596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1190596ba34bSRafael J. Wysocki  */
1191596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1192596ba34bSRafael J. Wysocki {
1193596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1194596ba34bSRafael J. Wysocki 	bool run_complete;
1195596ba34bSRafael J. Wysocki 
1196596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1197596ba34bSRafael J. Wysocki 
1198596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1199596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1200596ba34bSRafael J. Wysocki 		return;
1201596ba34bSRafael J. Wysocki 
1202596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1203596ba34bSRafael J. Wysocki 
1204596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1205596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1206596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1207596ba34bSRafael J. Wysocki 
1208596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1209596ba34bSRafael J. Wysocki 
1210596ba34bSRafael J. Wysocki 	if (run_complete) {
1211596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
12126f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1213596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
12146f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1215596ba34bSRafael J. Wysocki 	}
1216596ba34bSRafael J. Wysocki }
1217596ba34bSRafael J. Wysocki 
1218596ba34bSRafael J. Wysocki #else
1219596ba34bSRafael J. Wysocki 
1220596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1221596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
12220496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late		NULL
1223596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
12240496c8aeSRafael J. Wysocki #define pm_genpd_resume_early		NULL
1225596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1226596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1227596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
12280496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late		NULL
1229596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
12300496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early		NULL
1231596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1232596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1233596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1234596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1235596ba34bSRafael J. Wysocki 
1236596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1237596ba34bSRafael J. Wysocki 
1238f721889fSRafael J. Wysocki /**
1239b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1240f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1241f721889fSRafael J. Wysocki  * @dev: Device to be added.
1242b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1243f721889fSRafael J. Wysocki  */
1244b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1245b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1246f721889fSRafael J. Wysocki {
1247cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
12484605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1249f721889fSRafael J. Wysocki 	int ret = 0;
1250f721889fSRafael J. Wysocki 
1251f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1252f721889fSRafael J. Wysocki 
1253f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1254f721889fSRafael J. Wysocki 		return -EINVAL;
1255f721889fSRafael J. Wysocki 
12566ff7bb0dSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
12576ff7bb0dSRafael J. Wysocki 	if (!gpd_data)
12586ff7bb0dSRafael J. Wysocki 		return -ENOMEM;
12596ff7bb0dSRafael J. Wysocki 
12606ff7bb0dSRafael J. Wysocki 	mutex_init(&gpd_data->lock);
12616ff7bb0dSRafael J. Wysocki 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
12626ff7bb0dSRafael J. Wysocki 	dev_pm_qos_add_notifier(dev, &gpd_data->nb);
12636ff7bb0dSRafael J. Wysocki 
126417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1265f721889fSRafael J. Wysocki 
1266596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1267596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1268596ba34bSRafael J. Wysocki 		goto out;
1269596ba34bSRafael J. Wysocki 	}
1270596ba34bSRafael J. Wysocki 
12714605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
12724605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1273f721889fSRafael J. Wysocki 			ret = -EINVAL;
1274f721889fSRafael J. Wysocki 			goto out;
1275f721889fSRafael J. Wysocki 		}
1276f721889fSRafael J. Wysocki 
1277596ba34bSRafael J. Wysocki 	genpd->device_count++;
12786ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1279f721889fSRafael J. Wysocki 
12804605ab65SRafael J. Wysocki 	dev_pm_get_subsys_data(dev);
12816ff7bb0dSRafael J. Wysocki 
12826ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
12836ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
12846ff7bb0dSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
1285cd0ea672SRafael J. Wysocki 	dev->power.subsys_data->domain_data = &gpd_data->base;
1286cd0ea672SRafael J. Wysocki 	gpd_data->base.dev = dev;
1287cd0ea672SRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1288ca1d72f0SRafael J. Wysocki 	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
1289b02c999aSRafael J. Wysocki 	if (td)
1290b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1291f721889fSRafael J. Wysocki 
12926ff7bb0dSRafael J. Wysocki 	gpd_data->td.constraint_changed = true;
12936ff7bb0dSRafael J. Wysocki 	gpd_data->td.effective_constraint_ns = -1;
12946ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
12956ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
12966ff7bb0dSRafael J. Wysocki 
12976ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
12986ff7bb0dSRafael J. Wysocki 
12996ff7bb0dSRafael J. Wysocki 	return 0;
13006ff7bb0dSRafael J. Wysocki 
1301f721889fSRafael J. Wysocki  out:
130217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1303f721889fSRafael J. Wysocki 
13046ff7bb0dSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
13056ff7bb0dSRafael J. Wysocki 	kfree(gpd_data);
1306f721889fSRafael J. Wysocki 	return ret;
1307f721889fSRafael J. Wysocki }
1308f721889fSRafael J. Wysocki 
1309f721889fSRafael J. Wysocki /**
1310c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1311c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1312c8aa130bSThomas Abraham  *   the device is added to.
1313c8aa130bSThomas Abraham  * @dev: Device to be added.
1314c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1315c8aa130bSThomas Abraham  */
1316c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1317c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1318c8aa130bSThomas Abraham {
1319c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1320c8aa130bSThomas Abraham 
1321c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1322c8aa130bSThomas Abraham 
1323c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1324c8aa130bSThomas Abraham 		return -EINVAL;
1325c8aa130bSThomas Abraham 
1326c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1327c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1328c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1329c8aa130bSThomas Abraham 			genpd = gpd;
1330c8aa130bSThomas Abraham 			break;
1331c8aa130bSThomas Abraham 		}
1332c8aa130bSThomas Abraham 	}
1333c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1334c8aa130bSThomas Abraham 
1335c8aa130bSThomas Abraham 	if (!genpd)
1336c8aa130bSThomas Abraham 		return -EINVAL;
1337c8aa130bSThomas Abraham 
1338c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1339c8aa130bSThomas Abraham }
1340c8aa130bSThomas Abraham 
1341c8aa130bSThomas Abraham /**
1342f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1343f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1344f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1345f721889fSRafael J. Wysocki  */
1346f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1347f721889fSRafael J. Wysocki 			   struct device *dev)
1348f721889fSRafael J. Wysocki {
13496ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
13504605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1351efa69025SRafael J. Wysocki 	int ret = 0;
1352f721889fSRafael J. Wysocki 
1353f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1354f721889fSRafael J. Wysocki 
1355efa69025SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
1356efa69025SRafael J. Wysocki 	    ||  IS_ERR_OR_NULL(dev->pm_domain)
1357efa69025SRafael J. Wysocki 	    ||  pd_to_genpd(dev->pm_domain) != genpd)
1358f721889fSRafael J. Wysocki 		return -EINVAL;
1359f721889fSRafael J. Wysocki 
136017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1361f721889fSRafael J. Wysocki 
1362596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1363596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1364596ba34bSRafael J. Wysocki 		goto out;
1365596ba34bSRafael J. Wysocki 	}
1366596ba34bSRafael J. Wysocki 
13676ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
13686ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
13696ff7bb0dSRafael J. Wysocki 
13706ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1371f721889fSRafael J. Wysocki 	dev->pm_domain = NULL;
1372efa69025SRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1373efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
1374efa69025SRafael J. Wysocki 	dev->power.subsys_data->domain_data = NULL;
13756ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1376f721889fSRafael J. Wysocki 
13776ff7bb0dSRafael J. Wysocki 	gpd_data = to_gpd_data(pdd);
13786ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
13796ff7bb0dSRafael J. Wysocki 	pdd->dev = NULL;
13806ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
13816ff7bb0dSRafael J. Wysocki 
13826ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
13836ff7bb0dSRafael J. Wysocki 
13846ff7bb0dSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
13856ff7bb0dSRafael J. Wysocki 	kfree(gpd_data);
13866ff7bb0dSRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
13876ff7bb0dSRafael J. Wysocki 	return 0;
1388f721889fSRafael J. Wysocki 
1389596ba34bSRafael J. Wysocki  out:
139017b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1391f721889fSRafael J. Wysocki 
1392f721889fSRafael J. Wysocki 	return ret;
1393f721889fSRafael J. Wysocki }
1394f721889fSRafael J. Wysocki 
1395f721889fSRafael J. Wysocki /**
13961e78a0c7SRafael J. Wysocki  * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device.
13971e78a0c7SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
13981e78a0c7SRafael J. Wysocki  * @val: The new value of the device's "always on" flag.
13991e78a0c7SRafael J. Wysocki  */
14001e78a0c7SRafael J. Wysocki void pm_genpd_dev_always_on(struct device *dev, bool val)
14011e78a0c7SRafael J. Wysocki {
14021e78a0c7SRafael J. Wysocki 	struct pm_subsys_data *psd;
14031e78a0c7SRafael J. Wysocki 	unsigned long flags;
14041e78a0c7SRafael J. Wysocki 
14051e78a0c7SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
14061e78a0c7SRafael J. Wysocki 
14071e78a0c7SRafael J. Wysocki 	psd = dev_to_psd(dev);
14081e78a0c7SRafael J. Wysocki 	if (psd && psd->domain_data)
14091e78a0c7SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->always_on = val;
14101e78a0c7SRafael J. Wysocki 
14111e78a0c7SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
14121e78a0c7SRafael J. Wysocki }
14131e78a0c7SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);
14141e78a0c7SRafael J. Wysocki 
14151e78a0c7SRafael J. Wysocki /**
1416ca1d72f0SRafael J. Wysocki  * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
1417ca1d72f0SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
1418ca1d72f0SRafael J. Wysocki  * @val: The new value of the device's "need restore" flag.
1419ca1d72f0SRafael J. Wysocki  */
1420ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val)
1421ca1d72f0SRafael J. Wysocki {
1422ca1d72f0SRafael J. Wysocki 	struct pm_subsys_data *psd;
1423ca1d72f0SRafael J. Wysocki 	unsigned long flags;
1424ca1d72f0SRafael J. Wysocki 
1425ca1d72f0SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
1426ca1d72f0SRafael J. Wysocki 
1427ca1d72f0SRafael J. Wysocki 	psd = dev_to_psd(dev);
1428ca1d72f0SRafael J. Wysocki 	if (psd && psd->domain_data)
1429ca1d72f0SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->need_restore = val;
1430ca1d72f0SRafael J. Wysocki 
1431ca1d72f0SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1432ca1d72f0SRafael J. Wysocki }
1433ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
1434ca1d72f0SRafael J. Wysocki 
1435ca1d72f0SRafael J. Wysocki /**
1436f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1437f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1438bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1439f721889fSRafael J. Wysocki  */
1440f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1441bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1442f721889fSRafael J. Wysocki {
14435063ce15SRafael J. Wysocki 	struct gpd_link *link;
1444f721889fSRafael J. Wysocki 	int ret = 0;
1445f721889fSRafael J. Wysocki 
1446bc0403ffSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1447f721889fSRafael J. Wysocki 		return -EINVAL;
1448f721889fSRafael J. Wysocki 
144917b75ecaSRafael J. Wysocki  start:
145017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1451bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1452f721889fSRafael J. Wysocki 
1453bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1454bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1455bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
145617b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
145717b75ecaSRafael J. Wysocki 		goto start;
145817b75ecaSRafael J. Wysocki 	}
145917b75ecaSRafael J. Wysocki 
146017b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1461bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1462f721889fSRafael J. Wysocki 		ret = -EINVAL;
1463f721889fSRafael J. Wysocki 		goto out;
1464f721889fSRafael J. Wysocki 	}
1465f721889fSRafael J. Wysocki 
14664fcac10dSHuang Ying 	list_for_each_entry(link, &genpd->master_links, master_node) {
1467bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1468f721889fSRafael J. Wysocki 			ret = -EINVAL;
1469f721889fSRafael J. Wysocki 			goto out;
1470f721889fSRafael J. Wysocki 		}
1471f721889fSRafael J. Wysocki 	}
1472f721889fSRafael J. Wysocki 
14735063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
14745063ce15SRafael J. Wysocki 	if (!link) {
14755063ce15SRafael J. Wysocki 		ret = -ENOMEM;
14765063ce15SRafael J. Wysocki 		goto out;
14775063ce15SRafael J. Wysocki 	}
14785063ce15SRafael J. Wysocki 	link->master = genpd;
14795063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1480bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1481bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1482bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1483c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1484f721889fSRafael J. Wysocki 
1485f721889fSRafael J. Wysocki  out:
1486bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
148717b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1488f721889fSRafael J. Wysocki 
1489f721889fSRafael J. Wysocki 	return ret;
1490f721889fSRafael J. Wysocki }
1491f721889fSRafael J. Wysocki 
1492f721889fSRafael J. Wysocki /**
1493f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1494f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
14955063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1496f721889fSRafael J. Wysocki  */
1497f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
14985063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1499f721889fSRafael J. Wysocki {
15005063ce15SRafael J. Wysocki 	struct gpd_link *link;
1501f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1502f721889fSRafael J. Wysocki 
15035063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1504f721889fSRafael J. Wysocki 		return -EINVAL;
1505f721889fSRafael J. Wysocki 
150617b75ecaSRafael J. Wysocki  start:
150717b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1508f721889fSRafael J. Wysocki 
15095063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
15105063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1511f721889fSRafael J. Wysocki 			continue;
1512f721889fSRafael J. Wysocki 
1513f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1514f721889fSRafael J. Wysocki 
151517b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
151617b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
151717b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
151817b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
151917b75ecaSRafael J. Wysocki 			goto start;
152017b75ecaSRafael J. Wysocki 		}
152117b75ecaSRafael J. Wysocki 
15225063ce15SRafael J. Wysocki 		list_del(&link->master_node);
15235063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
15245063ce15SRafael J. Wysocki 		kfree(link);
152517b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1526f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1527f721889fSRafael J. Wysocki 
1528f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1529f721889fSRafael J. Wysocki 
1530f721889fSRafael J. Wysocki 		ret = 0;
1531f721889fSRafael J. Wysocki 		break;
1532f721889fSRafael J. Wysocki 	}
1533f721889fSRafael J. Wysocki 
153417b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1535f721889fSRafael J. Wysocki 
1536f721889fSRafael J. Wysocki 	return ret;
1537f721889fSRafael J. Wysocki }
1538f721889fSRafael J. Wysocki 
1539f721889fSRafael J. Wysocki /**
1540d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1541d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1542d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1543b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
1544d5e4cbfeSRafael J. Wysocki  */
1545b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1546b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1547d5e4cbfeSRafael J. Wysocki {
1548d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1549d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1550d5e4cbfeSRafael J. Wysocki 
1551d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data && ops))
1552d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1553d5e4cbfeSRafael J. Wysocki 
1554d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1555d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1556d5e4cbfeSRafael J. Wysocki 
1557d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1558d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1559d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1560d5e4cbfeSRafael J. Wysocki 
1561d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = *ops;
1562b02c999aSRafael J. Wysocki 		if (td)
1563b02c999aSRafael J. Wysocki 			gpd_data->td = *td;
1564d5e4cbfeSRafael J. Wysocki 	} else {
1565d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1566d5e4cbfeSRafael J. Wysocki 	}
1567d5e4cbfeSRafael J. Wysocki 
1568d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1569d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1570d5e4cbfeSRafael J. Wysocki 
1571d5e4cbfeSRafael J. Wysocki 	return ret;
1572d5e4cbfeSRafael J. Wysocki }
1573d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1574d5e4cbfeSRafael J. Wysocki 
1575d5e4cbfeSRafael J. Wysocki /**
1576b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1577d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1578b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
1579d5e4cbfeSRafael J. Wysocki  */
1580b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1581d5e4cbfeSRafael J. Wysocki {
1582d5e4cbfeSRafael J. Wysocki 	struct pm_domain_data *pdd;
1583d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1584d5e4cbfeSRafael J. Wysocki 
1585d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1586d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1587d5e4cbfeSRafael J. Wysocki 
1588d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1589d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1590d5e4cbfeSRafael J. Wysocki 
1591d5e4cbfeSRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1592d5e4cbfeSRafael J. Wysocki 	if (pdd) {
1593d5e4cbfeSRafael J. Wysocki 		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
1594d5e4cbfeSRafael J. Wysocki 
1595d5e4cbfeSRafael J. Wysocki 		gpd_data->ops = (struct gpd_dev_ops){ 0 };
1596b02c999aSRafael J. Wysocki 		if (clear_td)
1597b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
1598d5e4cbfeSRafael J. Wysocki 	} else {
1599d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1600d5e4cbfeSRafael J. Wysocki 	}
1601d5e4cbfeSRafael J. Wysocki 
1602d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1603d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1604d5e4cbfeSRafael J. Wysocki 
1605d5e4cbfeSRafael J. Wysocki 	return ret;
1606d5e4cbfeSRafael J. Wysocki }
1607b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1608d5e4cbfeSRafael J. Wysocki 
1609d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1610d23b9b00SRafael J. Wysocki 
1611d5e4cbfeSRafael J. Wysocki /**
1612ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1613ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1614ecf00475SRafael J. Wysocki  */
1615ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1616ecf00475SRafael J. Wysocki {
1617ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1618ecf00475SRafael J. Wysocki 
1619ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1620ecf00475SRafael J. Wysocki 	if (cb)
1621ecf00475SRafael J. Wysocki 		return cb(dev);
1622ecf00475SRafael J. Wysocki 
16230b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
16240b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_suspend;
16250b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
16260b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_suspend;
16270b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
16280b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_suspend;
16290b589741SRafael J. Wysocki 	else
16300b589741SRafael J. Wysocki 		cb = NULL;
1631ecf00475SRafael J. Wysocki 
16320b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
16330b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_suspend;
16340b589741SRafael J. Wysocki 
16350b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1636ecf00475SRafael J. Wysocki }
1637ecf00475SRafael J. Wysocki 
1638ecf00475SRafael J. Wysocki /**
1639ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1640ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1641ecf00475SRafael J. Wysocki  */
1642ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1643ecf00475SRafael J. Wysocki {
1644ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1645ecf00475SRafael J. Wysocki 
1646ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1647ecf00475SRafael J. Wysocki 	if (cb)
1648ecf00475SRafael J. Wysocki 		return cb(dev);
1649ecf00475SRafael J. Wysocki 
16500b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
16510b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_resume;
16520b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
16530b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_resume;
16540b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
16550b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_resume;
16560b589741SRafael J. Wysocki 	else
16570b589741SRafael J. Wysocki 		cb = NULL;
1658ecf00475SRafael J. Wysocki 
16590b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
16600b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_resume;
16610b589741SRafael J. Wysocki 
16620b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1663ecf00475SRafael J. Wysocki }
1664ecf00475SRafael J. Wysocki 
16650f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
16660f1d6986SRafael J. Wysocki 
1667ecf00475SRafael J. Wysocki /**
1668d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
1669d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1670d23b9b00SRafael J. Wysocki  */
1671d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
1672d23b9b00SRafael J. Wysocki {
1673c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
1674d23b9b00SRafael J. Wysocki 
1675d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
1676d23b9b00SRafael J. Wysocki }
1677d23b9b00SRafael J. Wysocki 
1678d23b9b00SRafael J. Wysocki /**
1679d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
1680d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1681d23b9b00SRafael J. Wysocki  */
1682d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
1683d23b9b00SRafael J. Wysocki {
1684c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
1685d23b9b00SRafael J. Wysocki 
16860496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_late(dev);
1687d23b9b00SRafael J. Wysocki }
1688d23b9b00SRafael J. Wysocki 
1689d23b9b00SRafael J. Wysocki /**
1690d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
1691d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1692d23b9b00SRafael J. Wysocki  */
1693d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
1694d23b9b00SRafael J. Wysocki {
1695c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
1696d23b9b00SRafael J. Wysocki 
16970496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_early(dev);
1698d23b9b00SRafael J. Wysocki }
1699d23b9b00SRafael J. Wysocki 
1700d23b9b00SRafael J. Wysocki /**
1701d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
1702d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1703d23b9b00SRafael J. Wysocki  */
1704d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
1705d23b9b00SRafael J. Wysocki {
1706c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
1707d23b9b00SRafael J. Wysocki 
1708d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
1709d23b9b00SRafael J. Wysocki }
1710d23b9b00SRafael J. Wysocki 
1711d23b9b00SRafael J. Wysocki /**
1712d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
1713d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1714d23b9b00SRafael J. Wysocki  */
1715d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
1716d23b9b00SRafael J. Wysocki {
1717d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
1718d23b9b00SRafael J. Wysocki 
1719d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
1720d23b9b00SRafael J. Wysocki }
1721d23b9b00SRafael J. Wysocki 
1722d23b9b00SRafael J. Wysocki /**
1723d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
1724d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1725d23b9b00SRafael J. Wysocki  */
1726d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
1727d23b9b00SRafael J. Wysocki {
1728d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
1729d23b9b00SRafael J. Wysocki 
17300496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_late(dev);
1731d23b9b00SRafael J. Wysocki }
1732d23b9b00SRafael J. Wysocki 
1733d23b9b00SRafael J. Wysocki /**
1734d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
1735d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1736d23b9b00SRafael J. Wysocki  */
1737d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
1738d23b9b00SRafael J. Wysocki {
1739d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
1740d23b9b00SRafael J. Wysocki 
17410496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_early(dev);
1742d23b9b00SRafael J. Wysocki }
1743d23b9b00SRafael J. Wysocki 
1744d23b9b00SRafael J. Wysocki /**
1745d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
1746d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1747d23b9b00SRafael J. Wysocki  */
1748d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
1749d23b9b00SRafael J. Wysocki {
1750d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
1751d23b9b00SRafael J. Wysocki 
1752d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
1753d23b9b00SRafael J. Wysocki }
1754d23b9b00SRafael J. Wysocki 
17550f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
17560f1d6986SRafael J. Wysocki 
17570f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
17580f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
17590f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
17600f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
17610f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
17620f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
17630f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
17640f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
17650f1d6986SRafael J. Wysocki 
17660f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
17670f1d6986SRafael J. Wysocki 
1768d23b9b00SRafael J. Wysocki /**
1769f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1770f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1771f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1772f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
1773f721889fSRafael J. Wysocki  */
1774f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
1775f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1776f721889fSRafael J. Wysocki {
1777f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1778f721889fSRafael J. Wysocki 		return;
1779f721889fSRafael J. Wysocki 
17805063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
17815063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
1782f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1783f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1784f721889fSRafael J. Wysocki 	genpd->gov = gov;
1785f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1786f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1787c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
178817b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
178917b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
1790c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
1791c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
1792596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1793221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
17946ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1795f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1796f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1797f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1798596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1799596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
18000496c8aeSRafael J. Wysocki 	genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
1801596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1802596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
18030496c8aeSRafael J. Wysocki 	genpd->domain.ops.resume_early = pm_genpd_resume_early;
1804596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1805596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
18060496c8aeSRafael J. Wysocki 	genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
1807596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1808596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
18090496c8aeSRafael J. Wysocki 	genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
1810596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1811d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
18120496c8aeSRafael J. Wysocki 	genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
1813d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
1814596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
18150496c8aeSRafael J. Wysocki 	genpd->domain.ops.restore_early = pm_genpd_resume_early;
1816d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
1817596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
1818ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
1819ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
1820c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
1821c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
1822c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
1823c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
1824d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
1825d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
1826d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
1827d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
18285125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
18295125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
18305125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
18315125bbf3SRafael J. Wysocki }
1832