xref: /openbmc/linux/drivers/base/power/domain.c (revision b3d3b9fb)
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 
568bc0251dSRafael J. Wysocki static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
578bc0251dSRafael J. Wysocki {
588bc0251dSRafael J. Wysocki 	struct generic_pm_domain *genpd = NULL, *gpd;
598bc0251dSRafael J. Wysocki 
608bc0251dSRafael J. Wysocki 	if (IS_ERR_OR_NULL(domain_name))
618bc0251dSRafael J. Wysocki 		return NULL;
628bc0251dSRafael J. Wysocki 
638bc0251dSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
648bc0251dSRafael J. Wysocki 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
658bc0251dSRafael J. Wysocki 		if (!strcmp(gpd->name, domain_name)) {
668bc0251dSRafael J. Wysocki 			genpd = gpd;
678bc0251dSRafael J. Wysocki 			break;
688bc0251dSRafael J. Wysocki 		}
698bc0251dSRafael J. Wysocki 	}
708bc0251dSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
718bc0251dSRafael J. Wysocki 	return genpd;
728bc0251dSRafael J. Wysocki }
738bc0251dSRafael J. Wysocki 
745248051bSRafael J. Wysocki #ifdef CONFIG_PM
755248051bSRafael J. Wysocki 
76b02c999aSRafael J. Wysocki struct generic_pm_domain *dev_to_genpd(struct device *dev)
775248051bSRafael J. Wysocki {
785248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
795248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
805248051bSRafael J. Wysocki 
81596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
825248051bSRafael J. Wysocki }
83f721889fSRafael J. Wysocki 
84d5e4cbfeSRafael J. Wysocki static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
85d5e4cbfeSRafael J. Wysocki {
860140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev,
870140d8bdSRafael J. Wysocki 					stop_latency_ns, "stop");
88d5e4cbfeSRafael J. Wysocki }
89d5e4cbfeSRafael J. Wysocki 
90d5e4cbfeSRafael J. Wysocki static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
91d5e4cbfeSRafael J. Wysocki {
920140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev,
930140d8bdSRafael J. Wysocki 					start_latency_ns, "start");
94d5e4cbfeSRafael J. Wysocki }
95d5e4cbfeSRafael J. Wysocki 
96c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
97f721889fSRafael J. Wysocki {
98c4bb3160SRafael J. Wysocki 	bool ret = false;
99c4bb3160SRafael J. Wysocki 
100c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
101c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
102c4bb3160SRafael J. Wysocki 
103c4bb3160SRafael J. Wysocki 	return ret;
104c4bb3160SRafael J. Wysocki }
105c4bb3160SRafael J. Wysocki 
106c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
107c4bb3160SRafael J. Wysocki {
108c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
109c4bb3160SRafael J. Wysocki 	smp_mb__after_atomic_inc();
110f721889fSRafael J. Wysocki }
111f721889fSRafael J. Wysocki 
11217b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
11317b75ecaSRafael J. Wysocki {
11417b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
11517b75ecaSRafael J. Wysocki 
11617b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
11717b75ecaSRafael J. Wysocki 	/*
11817b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
11917b75ecaSRafael J. Wysocki 	 * or the power off state.
12017b75ecaSRafael J. Wysocki 	 */
12117b75ecaSRafael J. Wysocki 	for (;;) {
12217b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
12317b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
124c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
125c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
12617b75ecaSRafael J. Wysocki 			break;
12717b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
12817b75ecaSRafael J. Wysocki 
12917b75ecaSRafael J. Wysocki 		schedule();
13017b75ecaSRafael J. Wysocki 
13117b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
13217b75ecaSRafael J. Wysocki 	}
13317b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
13417b75ecaSRafael J. Wysocki }
13517b75ecaSRafael J. Wysocki 
13617b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
13717b75ecaSRafael J. Wysocki {
13817b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
13917b75ecaSRafael J. Wysocki }
14017b75ecaSRafael J. Wysocki 
141c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
142c6d22b37SRafael J. Wysocki {
143c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
144c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
145c6d22b37SRafael J. Wysocki }
146c6d22b37SRafael J. Wysocki 
147cbc9ef02SRafael J. Wysocki static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
148cbc9ef02SRafael J. Wysocki {
149cbc9ef02SRafael J. Wysocki 	s64 usecs64;
150cbc9ef02SRafael J. Wysocki 
151cbc9ef02SRafael J. Wysocki 	if (!genpd->cpu_data)
152cbc9ef02SRafael J. Wysocki 		return;
153cbc9ef02SRafael J. Wysocki 
154cbc9ef02SRafael J. Wysocki 	usecs64 = genpd->power_on_latency_ns;
155cbc9ef02SRafael J. Wysocki 	do_div(usecs64, NSEC_PER_USEC);
156cbc9ef02SRafael J. Wysocki 	usecs64 += genpd->cpu_data->saved_exit_latency;
157cbc9ef02SRafael J. Wysocki 	genpd->cpu_data->idle_state->exit_latency = usecs64;
158cbc9ef02SRafael J. Wysocki }
159cbc9ef02SRafael J. Wysocki 
160f721889fSRafael J. Wysocki /**
1615063ce15SRafael J. Wysocki  * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
1625248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
1635248051bSRafael J. Wysocki  *
1645063ce15SRafael J. Wysocki  * Restore power to @genpd and all of its masters so that it is possible to
1655248051bSRafael J. Wysocki  * resume a device belonging to it.
1665248051bSRafael J. Wysocki  */
1678951ef02SSachin Kamat static int __pm_genpd_poweron(struct generic_pm_domain *genpd)
1683f241775SRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
1695248051bSRafael J. Wysocki {
1705063ce15SRafael J. Wysocki 	struct gpd_link *link;
1713f241775SRafael J. Wysocki 	DEFINE_WAIT(wait);
1725248051bSRafael J. Wysocki 	int ret = 0;
1735248051bSRafael J. Wysocki 
1745063ce15SRafael J. Wysocki 	/* If the domain's master is being waited for, we have to wait too. */
1753f241775SRafael J. Wysocki 	for (;;) {
1763f241775SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
1773f241775SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
17817877eb5SRafael J. Wysocki 		if (genpd->status != GPD_STATE_WAIT_MASTER)
1793f241775SRafael J. Wysocki 			break;
1803f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1813f241775SRafael J. Wysocki 
1823f241775SRafael J. Wysocki 		schedule();
1833f241775SRafael J. Wysocki 
18417b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
1853f241775SRafael J. Wysocki 	}
1863f241775SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
18717b75ecaSRafael J. Wysocki 
18817b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
189596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
1903f241775SRafael J. Wysocki 		return 0;
1915248051bSRafael J. Wysocki 
192c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
193c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
1943f241775SRafael J. Wysocki 		return 0;
195c6d22b37SRafael J. Wysocki 	}
196c6d22b37SRafael J. Wysocki 
197cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
198cbc9ef02SRafael J. Wysocki 		cpuidle_pause_and_lock();
199cbc9ef02SRafael J. Wysocki 		genpd->cpu_data->idle_state->disabled = true;
200cbc9ef02SRafael J. Wysocki 		cpuidle_resume_and_unlock();
201cbc9ef02SRafael J. Wysocki 		goto out;
202cbc9ef02SRafael J. Wysocki 	}
203cbc9ef02SRafael J. Wysocki 
2045063ce15SRafael J. Wysocki 	/*
2055063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
2065063ce15SRafael J. Wysocki 	 * executed, unless one of the masters' .power_on() callbacks fiddles
2075063ce15SRafael J. Wysocki 	 * with it.
2085063ce15SRafael J. Wysocki 	 */
2095063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
2105063ce15SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
21117877eb5SRafael J. Wysocki 		genpd->status = GPD_STATE_WAIT_MASTER;
2123c07cbc4SRafael J. Wysocki 
2135248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
2145248051bSRafael J. Wysocki 
2155063ce15SRafael J. Wysocki 		ret = pm_genpd_poweron(link->master);
2169e08cf42SRafael J. Wysocki 
2179e08cf42SRafael J. Wysocki 		mutex_lock(&genpd->lock);
2189e08cf42SRafael J. Wysocki 
2193f241775SRafael J. Wysocki 		/*
2203f241775SRafael J. Wysocki 		 * The "wait for parent" status is guaranteed not to change
2215063ce15SRafael J. Wysocki 		 * while the master is powering on.
2223f241775SRafael J. Wysocki 		 */
2233f241775SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
2243f241775SRafael J. Wysocki 		wake_up_all(&genpd->status_wait_queue);
2255063ce15SRafael J. Wysocki 		if (ret) {
2265063ce15SRafael J. Wysocki 			genpd_sd_counter_dec(link->master);
2279e08cf42SRafael J. Wysocki 			goto err;
2285248051bSRafael J. Wysocki 		}
2295063ce15SRafael J. Wysocki 	}
2305248051bSRafael J. Wysocki 
2319e08cf42SRafael J. Wysocki 	if (genpd->power_on) {
2320140d8bdSRafael J. Wysocki 		ktime_t time_start = ktime_get();
2330140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
2340140d8bdSRafael J. Wysocki 
235fe202fdeSRafael J. Wysocki 		ret = genpd->power_on(genpd);
2369e08cf42SRafael J. Wysocki 		if (ret)
2379e08cf42SRafael J. Wysocki 			goto err;
2380140d8bdSRafael J. Wysocki 
2390140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
240e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_on_latency_ns) {
2410140d8bdSRafael J. Wysocki 			genpd->power_on_latency_ns = elapsed_ns;
2426ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
243cbc9ef02SRafael J. Wysocki 			genpd_recalc_cpu_exit_latency(genpd);
244e84b2c20SRafael J. Wysocki 			if (genpd->name)
245e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-on latency exceeded, "
246e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
247e84b2c20SRafael J. Wysocki 					elapsed_ns);
248e84b2c20SRafael J. Wysocki 		}
2493c07cbc4SRafael J. Wysocki 	}
2505248051bSRafael J. Wysocki 
251cbc9ef02SRafael J. Wysocki  out:
2529e08cf42SRafael J. Wysocki 	genpd_set_active(genpd);
2539e08cf42SRafael J. Wysocki 
2543f241775SRafael J. Wysocki 	return 0;
2559e08cf42SRafael J. Wysocki 
2569e08cf42SRafael J. Wysocki  err:
2575063ce15SRafael J. Wysocki 	list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
2585063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
2599e08cf42SRafael J. Wysocki 
2603f241775SRafael J. Wysocki 	return ret;
2613f241775SRafael J. Wysocki }
2623f241775SRafael J. Wysocki 
2633f241775SRafael J. Wysocki /**
2645063ce15SRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its masters.
2653f241775SRafael J. Wysocki  * @genpd: PM domain to power up.
2663f241775SRafael J. Wysocki  */
2673f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd)
2683f241775SRafael J. Wysocki {
2693f241775SRafael J. Wysocki 	int ret;
2703f241775SRafael J. Wysocki 
2713f241775SRafael J. Wysocki 	mutex_lock(&genpd->lock);
2723f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
2733f241775SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
2743f241775SRafael J. Wysocki 	return ret;
2755248051bSRafael J. Wysocki }
2765248051bSRafael J. Wysocki 
2778bc0251dSRafael J. Wysocki /**
2788bc0251dSRafael J. Wysocki  * pm_genpd_name_poweron - Restore power to a given PM domain and its masters.
2798bc0251dSRafael J. Wysocki  * @domain_name: Name of the PM domain to power up.
2808bc0251dSRafael J. Wysocki  */
2818bc0251dSRafael J. Wysocki int pm_genpd_name_poweron(const char *domain_name)
2828bc0251dSRafael J. Wysocki {
2838bc0251dSRafael J. Wysocki 	struct generic_pm_domain *genpd;
2848bc0251dSRafael J. Wysocki 
2858bc0251dSRafael J. Wysocki 	genpd = pm_genpd_lookup_name(domain_name);
2868bc0251dSRafael J. Wysocki 	return genpd ? pm_genpd_poweron(genpd) : -EINVAL;
2878bc0251dSRafael J. Wysocki }
2888bc0251dSRafael J. Wysocki 
2895248051bSRafael J. Wysocki #endif /* CONFIG_PM */
2905248051bSRafael J. Wysocki 
2915248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
2925248051bSRafael J. Wysocki 
293b3d3b9fbSSachin Kamat static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd,
294b3d3b9fbSSachin Kamat 				     struct device *dev)
295b3d3b9fbSSachin Kamat {
296b3d3b9fbSSachin Kamat 	return GENPD_DEV_CALLBACK(genpd, int, start, dev);
297b3d3b9fbSSachin Kamat }
298b3d3b9fbSSachin Kamat 
2998e9afafdSRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev)
3008e9afafdSRafael J. Wysocki {
3018e9afafdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev,
3028e9afafdSRafael J. Wysocki 					save_state_latency_ns, "state save");
3038e9afafdSRafael J. Wysocki }
3048e9afafdSRafael J. Wysocki 
3058e9afafdSRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev)
3068e9afafdSRafael J. Wysocki {
3078e9afafdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev,
3088e9afafdSRafael J. Wysocki 					restore_state_latency_ns,
3098e9afafdSRafael J. Wysocki 					"state restore");
3108e9afafdSRafael J. Wysocki }
3118e9afafdSRafael J. Wysocki 
3126ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
3136ff7bb0dSRafael J. Wysocki 				     unsigned long val, void *ptr)
3146ff7bb0dSRafael J. Wysocki {
3156ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
3166ff7bb0dSRafael J. Wysocki 	struct device *dev;
3176ff7bb0dSRafael J. Wysocki 
3186ff7bb0dSRafael J. Wysocki 	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
3196ff7bb0dSRafael J. Wysocki 
3206ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
3216ff7bb0dSRafael J. Wysocki 	dev = gpd_data->base.dev;
3226ff7bb0dSRafael J. Wysocki 	if (!dev) {
3236ff7bb0dSRafael J. Wysocki 		mutex_unlock(&gpd_data->lock);
3246ff7bb0dSRafael J. Wysocki 		return NOTIFY_DONE;
3256ff7bb0dSRafael J. Wysocki 	}
3266ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
3276ff7bb0dSRafael J. Wysocki 
3286ff7bb0dSRafael J. Wysocki 	for (;;) {
3296ff7bb0dSRafael J. Wysocki 		struct generic_pm_domain *genpd;
3306ff7bb0dSRafael J. Wysocki 		struct pm_domain_data *pdd;
3316ff7bb0dSRafael J. Wysocki 
3326ff7bb0dSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
3336ff7bb0dSRafael J. Wysocki 
3346ff7bb0dSRafael J. Wysocki 		pdd = dev->power.subsys_data ?
3356ff7bb0dSRafael J. Wysocki 				dev->power.subsys_data->domain_data : NULL;
3361d5fcfecSRafael J. Wysocki 		if (pdd && pdd->dev) {
3376ff7bb0dSRafael J. Wysocki 			to_gpd_data(pdd)->td.constraint_changed = true;
3386ff7bb0dSRafael J. Wysocki 			genpd = dev_to_genpd(dev);
3396ff7bb0dSRafael J. Wysocki 		} else {
3406ff7bb0dSRafael J. Wysocki 			genpd = ERR_PTR(-ENODATA);
3416ff7bb0dSRafael J. Wysocki 		}
3426ff7bb0dSRafael J. Wysocki 
3436ff7bb0dSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
3446ff7bb0dSRafael J. Wysocki 
3456ff7bb0dSRafael J. Wysocki 		if (!IS_ERR(genpd)) {
3466ff7bb0dSRafael J. Wysocki 			mutex_lock(&genpd->lock);
3476ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
3486ff7bb0dSRafael J. Wysocki 			mutex_unlock(&genpd->lock);
3496ff7bb0dSRafael J. Wysocki 		}
3506ff7bb0dSRafael J. Wysocki 
3516ff7bb0dSRafael J. Wysocki 		dev = dev->parent;
3526ff7bb0dSRafael J. Wysocki 		if (!dev || dev->power.ignore_children)
3536ff7bb0dSRafael J. Wysocki 			break;
3546ff7bb0dSRafael J. Wysocki 	}
3556ff7bb0dSRafael J. Wysocki 
3566ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
3576ff7bb0dSRafael J. Wysocki }
3586ff7bb0dSRafael J. Wysocki 
3595248051bSRafael J. Wysocki /**
360f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
3614605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to save the state of.
362f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
363f721889fSRafael J. Wysocki  */
3644605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd,
365f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
36617b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
367f721889fSRafael J. Wysocki {
368cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
3694605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
370f721889fSRafael J. Wysocki 	int ret = 0;
371f721889fSRafael J. Wysocki 
372cd0ea672SRafael J. Wysocki 	if (gpd_data->need_restore)
373f721889fSRafael J. Wysocki 		return 0;
374f721889fSRafael J. Wysocki 
37517b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
37617b75ecaSRafael J. Wysocki 
377d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
378ecf00475SRafael J. Wysocki 	ret = genpd_save_dev(genpd, dev);
379d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
380f721889fSRafael J. Wysocki 
38117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
38217b75ecaSRafael J. Wysocki 
383f721889fSRafael J. Wysocki 	if (!ret)
384cd0ea672SRafael J. Wysocki 		gpd_data->need_restore = true;
385f721889fSRafael J. Wysocki 
386f721889fSRafael J. Wysocki 	return ret;
387f721889fSRafael J. Wysocki }
388f721889fSRafael J. Wysocki 
389f721889fSRafael J. Wysocki /**
390f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
3914605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to restore the state of.
392f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
393f721889fSRafael J. Wysocki  */
3944605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
395f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
39617b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
397f721889fSRafael J. Wysocki {
398cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
3994605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
40080de3d7fSRafael J. Wysocki 	bool need_restore = gpd_data->need_restore;
401f721889fSRafael J. Wysocki 
40280de3d7fSRafael J. Wysocki 	gpd_data->need_restore = false;
40317b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
40417b75ecaSRafael J. Wysocki 
405d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
40680de3d7fSRafael J. Wysocki 	if (need_restore)
407ecf00475SRafael J. Wysocki 		genpd_restore_dev(genpd, dev);
408f721889fSRafael J. Wysocki 
40917b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
410f721889fSRafael J. Wysocki }
411f721889fSRafael J. Wysocki 
412f721889fSRafael J. Wysocki /**
413c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
414c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
415c6d22b37SRafael J. Wysocki  *
416c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
417c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
418c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
419c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
420c6d22b37SRafael J. Wysocki  */
421c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
422c6d22b37SRafael J. Wysocki {
42317877eb5SRafael J. Wysocki 	return genpd->status == GPD_STATE_WAIT_MASTER
4243f241775SRafael J. Wysocki 		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
425c6d22b37SRafael J. Wysocki }
426c6d22b37SRafael J. Wysocki 
427c6d22b37SRafael J. Wysocki /**
42856375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
42956375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
43056375fd4SRafael J. Wysocki  *
43156375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
43256375fd4SRafael J. Wysocki  * before.
43356375fd4SRafael J. Wysocki  */
4340bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
43556375fd4SRafael J. Wysocki {
43656375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
43756375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
43856375fd4SRafael J. Wysocki }
43956375fd4SRafael J. Wysocki 
44056375fd4SRafael J. Wysocki /**
441f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
442f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
443f721889fSRafael J. Wysocki  *
444f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
445f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
446f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
447f721889fSRafael J. Wysocki  */
448f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
44917b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
450f721889fSRafael J. Wysocki {
4514605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
4525063ce15SRafael J. Wysocki 	struct gpd_link *link;
453f721889fSRafael J. Wysocki 	unsigned int not_suspended;
454c6d22b37SRafael J. Wysocki 	int ret = 0;
455f721889fSRafael J. Wysocki 
456c6d22b37SRafael J. Wysocki  start:
457c6d22b37SRafael J. Wysocki 	/*
458c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
459c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
4605063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
461c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
4623f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
463c6d22b37SRafael J. Wysocki 	 */
4643f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
46517877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
4663f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
467f721889fSRafael J. Wysocki 		return 0;
468f721889fSRafael J. Wysocki 
469c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
470f721889fSRafael J. Wysocki 		return -EBUSY;
471f721889fSRafael J. Wysocki 
472f721889fSRafael J. Wysocki 	not_suspended = 0;
4734605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
4740aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
475feb70af0SRafael J. Wysocki 		    || pdd->dev->power.irq_safe))
476f721889fSRafael J. Wysocki 			not_suspended++;
477f721889fSRafael J. Wysocki 
478f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
479f721889fSRafael J. Wysocki 		return -EBUSY;
480f721889fSRafael J. Wysocki 
481c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
482c6d22b37SRafael J. Wysocki 		/*
483c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
484c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
485c6d22b37SRafael J. Wysocki 		 */
486c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
487c6d22b37SRafael J. Wysocki 		return 0;
488c6d22b37SRafael J. Wysocki 	}
489c6d22b37SRafael J. Wysocki 
490f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
491f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
492f721889fSRafael J. Wysocki 			return -EAGAIN;
493f721889fSRafael J. Wysocki 	}
494f721889fSRafael J. Wysocki 
49517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
496c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
49717b75ecaSRafael J. Wysocki 
4984605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
4993c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
5004605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
5013f241775SRafael J. Wysocki 
5023f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
5033f241775SRafael J. Wysocki 			goto out;
5043f241775SRafael J. Wysocki 
505697a7f37SRafael J. Wysocki 		if (ret) {
506697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
507697a7f37SRafael J. Wysocki 			goto out;
508697a7f37SRafael J. Wysocki 		}
509f721889fSRafael J. Wysocki 
510c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
511c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
512c6d22b37SRafael J. Wysocki 			goto start;
513c6d22b37SRafael J. Wysocki 		}
514c6d22b37SRafael J. Wysocki 	}
51517b75ecaSRafael J. Wysocki 
516cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
517cbc9ef02SRafael J. Wysocki 		/*
518cbc9ef02SRafael J. Wysocki 		 * If cpu_data is set, cpuidle should turn the domain off when
519cbc9ef02SRafael J. Wysocki 		 * the CPU in it is idle.  In that case we don't decrement the
520cbc9ef02SRafael J. Wysocki 		 * subdomain counts of the master domains, so that power is not
521cbc9ef02SRafael J. Wysocki 		 * removed from the current domain prematurely as a result of
522cbc9ef02SRafael J. Wysocki 		 * cutting off the masters' power.
523cbc9ef02SRafael J. Wysocki 		 */
524cbc9ef02SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
525cbc9ef02SRafael J. Wysocki 		cpuidle_pause_and_lock();
526cbc9ef02SRafael J. Wysocki 		genpd->cpu_data->idle_state->disabled = false;
527cbc9ef02SRafael J. Wysocki 		cpuidle_resume_and_unlock();
528cbc9ef02SRafael J. Wysocki 		goto out;
529cbc9ef02SRafael J. Wysocki 	}
530cbc9ef02SRafael J. Wysocki 
5313c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
5320140d8bdSRafael J. Wysocki 		ktime_t time_start;
5330140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
5340140d8bdSRafael J. Wysocki 
5353c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
5363c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
537c6d22b37SRafael J. Wysocki 			goto out;
538c6d22b37SRafael J. Wysocki 		}
53917b75ecaSRafael J. Wysocki 
5400140d8bdSRafael J. Wysocki 		time_start = ktime_get();
5410140d8bdSRafael J. Wysocki 
5423c07cbc4SRafael J. Wysocki 		/*
5435063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
5445063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
5453c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
5463c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
5473c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
5483c07cbc4SRafael J. Wysocki 		 * happen very often).
5493c07cbc4SRafael J. Wysocki 		 */
550d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
551d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
552d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
553d2805402SRafael J. Wysocki 			goto out;
554d2805402SRafael J. Wysocki 		}
5550140d8bdSRafael J. Wysocki 
5560140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
557e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_off_latency_ns) {
5580140d8bdSRafael J. Wysocki 			genpd->power_off_latency_ns = elapsed_ns;
5596ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
560e84b2c20SRafael J. Wysocki 			if (genpd->name)
561e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-off latency exceeded, "
562e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
563e84b2c20SRafael J. Wysocki 					elapsed_ns);
564e84b2c20SRafael J. Wysocki 		}
565d2805402SRafael J. Wysocki 	}
566f721889fSRafael J. Wysocki 
56717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
568221e9b58SRafael J. Wysocki 
5695063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
5705063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
5715063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
5725063ce15SRafael J. Wysocki 	}
57317b75ecaSRafael J. Wysocki 
574c6d22b37SRafael J. Wysocki  out:
575c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
576c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
577c6d22b37SRafael J. Wysocki 	return ret;
578f721889fSRafael J. Wysocki }
579f721889fSRafael J. Wysocki 
580f721889fSRafael J. Wysocki /**
581f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
582f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
583f721889fSRafael J. Wysocki  */
584f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
585f721889fSRafael J. Wysocki {
586f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
587f721889fSRafael J. Wysocki 
588f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
589f721889fSRafael J. Wysocki 
59017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
591f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
59217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
593f721889fSRafael J. Wysocki }
594f721889fSRafael J. Wysocki 
595f721889fSRafael J. Wysocki /**
596f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
597f721889fSRafael J. Wysocki  * @dev: Device to suspend.
598f721889fSRafael J. Wysocki  *
599f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
600f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
601f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
602f721889fSRafael J. Wysocki  */
603f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
604f721889fSRafael J. Wysocki {
605f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
606b02c999aSRafael J. Wysocki 	bool (*stop_ok)(struct device *__dev);
607d5e4cbfeSRafael J. Wysocki 	int ret;
608f721889fSRafael J. Wysocki 
609f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
610f721889fSRafael J. Wysocki 
6115248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
6125248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
613f721889fSRafael J. Wysocki 		return -EINVAL;
614f721889fSRafael J. Wysocki 
6150aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
6160aa2a221SRafael J. Wysocki 
617b02c999aSRafael J. Wysocki 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
618b02c999aSRafael J. Wysocki 	if (stop_ok && !stop_ok(dev))
619b02c999aSRafael J. Wysocki 		return -EBUSY;
620b02c999aSRafael J. Wysocki 
621d5e4cbfeSRafael J. Wysocki 	ret = genpd_stop_dev(genpd, dev);
622f721889fSRafael J. Wysocki 	if (ret)
62317b75ecaSRafael J. Wysocki 		return ret;
62417b75ecaSRafael J. Wysocki 
6250aa2a221SRafael J. Wysocki 	/*
6260aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
6270aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
6280aa2a221SRafael J. Wysocki 	 */
6290aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
6300aa2a221SRafael J. Wysocki 		return 0;
6310aa2a221SRafael J. Wysocki 
632c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
633f721889fSRafael J. Wysocki 	genpd->in_progress++;
634f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
635f721889fSRafael J. Wysocki 	genpd->in_progress--;
636c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
637f721889fSRafael J. Wysocki 
638f721889fSRafael J. Wysocki 	return 0;
639f721889fSRafael J. Wysocki }
640f721889fSRafael J. Wysocki 
641f721889fSRafael J. Wysocki /**
642f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
643f721889fSRafael J. Wysocki  * @dev: Device to resume.
644f721889fSRafael J. Wysocki  *
645f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
646f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
647f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
648f721889fSRafael J. Wysocki  */
649f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
650f721889fSRafael J. Wysocki {
651f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
652c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
653f721889fSRafael J. Wysocki 	int ret;
654f721889fSRafael J. Wysocki 
655f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
656f721889fSRafael J. Wysocki 
6575248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
6585248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
659f721889fSRafael J. Wysocki 		return -EINVAL;
660f721889fSRafael J. Wysocki 
6610aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
6620aa2a221SRafael J. Wysocki 
6630aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
6640aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
665e2e3e4e5SRafael J. Wysocki 		return genpd_start_dev_no_timing(genpd, dev);
6660aa2a221SRafael J. Wysocki 
667c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
6683f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
6693f241775SRafael J. Wysocki 	if (ret) {
6703f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
6713f241775SRafael J. Wysocki 		return ret;
6723f241775SRafael J. Wysocki 	}
67317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
674c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
675c6d22b37SRafael J. Wysocki 	for (;;) {
676c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
677c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
678c6d22b37SRafael J. Wysocki 		/*
679c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
680c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
681c6d22b37SRafael J. Wysocki 		 * not wait.
682c6d22b37SRafael J. Wysocki 		 */
683c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
684c6d22b37SRafael J. Wysocki 			break;
685c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
686c6d22b37SRafael J. Wysocki 
687c6d22b37SRafael J. Wysocki 		schedule();
688c6d22b37SRafael J. Wysocki 
689c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
690c6d22b37SRafael J. Wysocki 	}
691c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
692cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
693c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
694c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
69517b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
696c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
69717b75ecaSRafael J. Wysocki 
698f721889fSRafael J. Wysocki 	return 0;
699f721889fSRafael J. Wysocki }
700f721889fSRafael J. Wysocki 
70117f2ae7fSRafael J. Wysocki /**
70217f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
70317f2ae7fSRafael J. Wysocki  */
70417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
70517f2ae7fSRafael J. Wysocki {
70617f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
70717f2ae7fSRafael J. Wysocki 
70817f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
70917f2ae7fSRafael J. Wysocki 
71017f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
71117f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
71217f2ae7fSRafael J. Wysocki 
71317f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
71417f2ae7fSRafael J. Wysocki }
71517f2ae7fSRafael J. Wysocki 
716f721889fSRafael J. Wysocki #else
717f721889fSRafael J. Wysocki 
7186ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
7196ff7bb0dSRafael J. Wysocki 					    unsigned long val, void *ptr)
7206ff7bb0dSRafael J. Wysocki {
7216ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
7226ff7bb0dSRafael J. Wysocki }
7236ff7bb0dSRafael J. Wysocki 
724f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
725f721889fSRafael J. Wysocki 
726f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
727f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
728f721889fSRafael J. Wysocki 
729f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
730f721889fSRafael J. Wysocki 
731596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
732596ba34bSRafael J. Wysocki 
73377f827deSRafael J. Wysocki /**
73477f827deSRafael J. Wysocki  * pm_genpd_present - Check if the given PM domain has been initialized.
73577f827deSRafael J. Wysocki  * @genpd: PM domain to check.
73677f827deSRafael J. Wysocki  */
73777f827deSRafael J. Wysocki static bool pm_genpd_present(struct generic_pm_domain *genpd)
73877f827deSRafael J. Wysocki {
73977f827deSRafael J. Wysocki 	struct generic_pm_domain *gpd;
74077f827deSRafael J. Wysocki 
74177f827deSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
74277f827deSRafael J. Wysocki 		return false;
74377f827deSRafael J. Wysocki 
74477f827deSRafael J. Wysocki 	list_for_each_entry(gpd, &gpd_list, gpd_list_node)
74577f827deSRafael J. Wysocki 		if (gpd == genpd)
74677f827deSRafael J. Wysocki 			return true;
74777f827deSRafael J. Wysocki 
74877f827deSRafael J. Wysocki 	return false;
74977f827deSRafael J. Wysocki }
75077f827deSRafael J. Wysocki 
751d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
752d5e4cbfeSRafael J. Wysocki 				    struct device *dev)
753d5e4cbfeSRafael J. Wysocki {
754d5e4cbfeSRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
755d5e4cbfeSRafael J. Wysocki }
756d5e4cbfeSRafael J. Wysocki 
757d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
758d23b9b00SRafael J. Wysocki {
759d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
760d23b9b00SRafael J. Wysocki }
761d23b9b00SRafael J. Wysocki 
762d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
763d23b9b00SRafael J. Wysocki {
764d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
765d23b9b00SRafael J. Wysocki }
766d23b9b00SRafael J. Wysocki 
767d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
768d23b9b00SRafael J. Wysocki {
769d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
770d23b9b00SRafael J. Wysocki }
771d23b9b00SRafael J. Wysocki 
772d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
773d23b9b00SRafael J. Wysocki {
774d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
775d23b9b00SRafael J. Wysocki }
776d23b9b00SRafael J. Wysocki 
777d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
778d23b9b00SRafael J. Wysocki {
779d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
780d23b9b00SRafael J. Wysocki }
781d23b9b00SRafael J. Wysocki 
782d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
783d23b9b00SRafael J. Wysocki {
784d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
785d23b9b00SRafael J. Wysocki }
786d23b9b00SRafael J. Wysocki 
787d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
788d23b9b00SRafael J. Wysocki {
789d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
790d23b9b00SRafael J. Wysocki }
791d23b9b00SRafael J. Wysocki 
792d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
793d23b9b00SRafael J. Wysocki {
794d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
795d23b9b00SRafael J. Wysocki }
796d23b9b00SRafael J. Wysocki 
797596ba34bSRafael J. Wysocki /**
7985063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
799596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
800596ba34bSRafael J. Wysocki  *
801596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
8025063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
803596ba34bSRafael J. Wysocki  *
80477f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
80577f827deSRafael J. Wysocki  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
80677f827deSRafael J. Wysocki  * executed sequentially, so it is guaranteed that it will never run twice in
80777f827deSRafael J. Wysocki  * parallel).
808596ba34bSRafael J. Wysocki  */
809596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
810596ba34bSRafael J. Wysocki {
8115063ce15SRafael J. Wysocki 	struct gpd_link *link;
812596ba34bSRafael J. Wysocki 
81317b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
814596ba34bSRafael J. Wysocki 		return;
815596ba34bSRafael J. Wysocki 
816c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
817c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
818596ba34bSRafael J. Wysocki 		return;
819596ba34bSRafael J. Wysocki 
820596ba34bSRafael J. Wysocki 	if (genpd->power_off)
821596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
822596ba34bSRafael J. Wysocki 
82317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
8245063ce15SRafael J. Wysocki 
8255063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
8265063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
8275063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
828596ba34bSRafael J. Wysocki 	}
829596ba34bSRafael J. Wysocki }
830596ba34bSRafael J. Wysocki 
831596ba34bSRafael J. Wysocki /**
832802d8b49SRafael J. Wysocki  * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
833802d8b49SRafael J. Wysocki  * @genpd: PM domain to power on.
834802d8b49SRafael J. Wysocki  *
83577f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
83677f827deSRafael J. Wysocki  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
83777f827deSRafael J. Wysocki  * executed sequentially, so it is guaranteed that it will never run twice in
83877f827deSRafael J. Wysocki  * parallel).
839802d8b49SRafael J. Wysocki  */
840802d8b49SRafael J. Wysocki static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
841802d8b49SRafael J. Wysocki {
842802d8b49SRafael J. Wysocki 	struct gpd_link *link;
843802d8b49SRafael J. Wysocki 
844802d8b49SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF)
845802d8b49SRafael J. Wysocki 		return;
846802d8b49SRafael J. Wysocki 
847802d8b49SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
848802d8b49SRafael J. Wysocki 		pm_genpd_sync_poweron(link->master);
849802d8b49SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
850802d8b49SRafael J. Wysocki 	}
851802d8b49SRafael J. Wysocki 
852802d8b49SRafael J. Wysocki 	if (genpd->power_on)
853802d8b49SRafael J. Wysocki 		genpd->power_on(genpd);
854802d8b49SRafael J. Wysocki 
855802d8b49SRafael J. Wysocki 	genpd->status = GPD_STATE_ACTIVE;
856802d8b49SRafael J. Wysocki }
857802d8b49SRafael J. Wysocki 
858802d8b49SRafael J. Wysocki /**
8594ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
8604ecd6e65SRafael J. Wysocki  * @dev: Device to check.
8614ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
8624ecd6e65SRafael J. Wysocki  *
8634ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
8644ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
8654ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
8664ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
8674ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
8684ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
8694ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
8704ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
8714ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
8724ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
8734ecd6e65SRafael J. Wysocki  */
8744ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
8754ecd6e65SRafael J. Wysocki {
8764ecd6e65SRafael J. Wysocki 	bool active_wakeup;
8774ecd6e65SRafael J. Wysocki 
8784ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
8794ecd6e65SRafael J. Wysocki 		return false;
8804ecd6e65SRafael J. Wysocki 
881d5e4cbfeSRafael J. Wysocki 	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
8824ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
8834ecd6e65SRafael J. Wysocki }
8844ecd6e65SRafael J. Wysocki 
8854ecd6e65SRafael J. Wysocki /**
886596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
887596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
888596ba34bSRafael J. Wysocki  *
889596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
890596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
891596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
892596ba34bSRafael J. Wysocki  * consisting of I/O devices.
893596ba34bSRafael J. Wysocki  */
894596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
895596ba34bSRafael J. Wysocki {
896596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
897b6c10c84SRafael J. Wysocki 	int ret;
898596ba34bSRafael J. Wysocki 
899596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
900596ba34bSRafael J. Wysocki 
901596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
902596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
903596ba34bSRafael J. Wysocki 		return -EINVAL;
904596ba34bSRafael J. Wysocki 
90517b75ecaSRafael J. Wysocki 	/*
90617b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
90717b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
90817b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
90917b75ecaSRafael J. Wysocki 	 */
91017b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
91117b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
91217b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
91317b75ecaSRafael J. Wysocki 
91417b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
91517b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
91617b75ecaSRafael J. Wysocki 		return -EBUSY;
91717b75ecaSRafael J. Wysocki 	}
91817b75ecaSRafael J. Wysocki 
9194ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
9204ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
9214ecd6e65SRafael J. Wysocki 
92217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
923596ba34bSRafael J. Wysocki 
92465533bbfSRafael J. Wysocki 	if (genpd->prepared_count++ == 0) {
92565533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
92617b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
92765533bbfSRafael J. Wysocki 	}
92817b75ecaSRafael J. Wysocki 
92917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
930596ba34bSRafael J. Wysocki 
931596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
93217b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
933596ba34bSRafael J. Wysocki 		return 0;
934596ba34bSRafael J. Wysocki 	}
935596ba34bSRafael J. Wysocki 
936596ba34bSRafael J. Wysocki 	/*
93717b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
93817b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
939d5e4cbfeSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
94017b75ecaSRafael J. Wysocki 	 * to make it operational.
941596ba34bSRafael J. Wysocki 	 */
94217b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
943596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
944596ba34bSRafael J. Wysocki 
945b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
946b6c10c84SRafael J. Wysocki 	if (ret) {
947b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
948b6c10c84SRafael J. Wysocki 
949b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
950b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
951b6c10c84SRafael J. Wysocki 
952b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
95317b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
954b6c10c84SRafael J. Wysocki 	}
95517b75ecaSRafael J. Wysocki 
95617b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
957b6c10c84SRafael J. Wysocki 	return ret;
958596ba34bSRafael J. Wysocki }
959596ba34bSRafael J. Wysocki 
960596ba34bSRafael J. Wysocki /**
961596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
962596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
963596ba34bSRafael J. Wysocki  *
964596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
965596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
966596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
967596ba34bSRafael J. Wysocki  */
968596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
969596ba34bSRafael J. Wysocki {
970596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
971596ba34bSRafael J. Wysocki 
972596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
973596ba34bSRafael J. Wysocki 
974596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
975596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
976596ba34bSRafael J. Wysocki 		return -EINVAL;
977596ba34bSRafael J. Wysocki 
978d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
979596ba34bSRafael J. Wysocki }
980596ba34bSRafael J. Wysocki 
981596ba34bSRafael J. Wysocki /**
9820496c8aeSRafael J. Wysocki  * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
983596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
984596ba34bSRafael J. Wysocki  *
985596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
986596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
987596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
988596ba34bSRafael J. Wysocki  */
9890496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev)
990596ba34bSRafael J. Wysocki {
991596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
992596ba34bSRafael J. Wysocki 
993596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
994596ba34bSRafael J. Wysocki 
995596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
996596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
997596ba34bSRafael J. Wysocki 		return -EINVAL;
998596ba34bSRafael J. Wysocki 
9990496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
10000496c8aeSRafael J. Wysocki }
1001596ba34bSRafael J. Wysocki 
10020496c8aeSRafael J. Wysocki /**
10030496c8aeSRafael J. Wysocki  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
10040496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
10050496c8aeSRafael J. Wysocki  *
10060496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
10070496c8aeSRafael J. Wysocki  * been stopped.
10080496c8aeSRafael J. Wysocki  */
10090496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
10100496c8aeSRafael J. Wysocki {
10110496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1012596ba34bSRafael J. Wysocki 
10130496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10140496c8aeSRafael J. Wysocki 
10150496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10160496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10170496c8aeSRafael J. Wysocki 		return -EINVAL;
10180496c8aeSRafael J. Wysocki 
1019dbf37414SRafael J. Wysocki 	if (genpd->suspend_power_off
10200496c8aeSRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
1021d4f2d87aSRafael J. Wysocki 		return 0;
1022d4f2d87aSRafael J. Wysocki 
1023d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
1024596ba34bSRafael J. Wysocki 
1025596ba34bSRafael J. Wysocki 	/*
1026596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1027596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1028596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1029596ba34bSRafael J. Wysocki 	 */
1030596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
1031596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
1032596ba34bSRafael J. Wysocki 
1033596ba34bSRafael J. Wysocki 	return 0;
1034596ba34bSRafael J. Wysocki }
1035596ba34bSRafael J. Wysocki 
1036596ba34bSRafael J. Wysocki /**
10370496c8aeSRafael J. Wysocki  * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
1038596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1039596ba34bSRafael J. Wysocki  *
10400496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
1041596ba34bSRafael J. Wysocki  */
1042596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
1043596ba34bSRafael J. Wysocki {
1044596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1045596ba34bSRafael J. Wysocki 
1046596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1047596ba34bSRafael J. Wysocki 
1048596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1049596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1050596ba34bSRafael J. Wysocki 		return -EINVAL;
1051596ba34bSRafael J. Wysocki 
1052dbf37414SRafael J. Wysocki 	if (genpd->suspend_power_off
1053cc85b207SRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
1054596ba34bSRafael J. Wysocki 		return 0;
1055596ba34bSRafael J. Wysocki 
1056596ba34bSRafael J. Wysocki 	/*
1057596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1058596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1059596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1060596ba34bSRafael J. Wysocki 	 */
1061802d8b49SRafael J. Wysocki 	pm_genpd_sync_poweron(genpd);
1062596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
1063596ba34bSRafael J. Wysocki 
10640496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1065596ba34bSRafael J. Wysocki }
1066596ba34bSRafael J. Wysocki 
1067596ba34bSRafael J. Wysocki /**
10680496c8aeSRafael J. Wysocki  * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
10690496c8aeSRafael J. Wysocki  * @dev: Device to resume.
10700496c8aeSRafael J. Wysocki  *
10710496c8aeSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
10720496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10730496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10740496c8aeSRafael J. Wysocki  * devices.
10750496c8aeSRafael J. Wysocki  */
10760496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev)
10770496c8aeSRafael J. Wysocki {
10780496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
10790496c8aeSRafael J. Wysocki 
10800496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10810496c8aeSRafael J. Wysocki 
10820496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10830496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10840496c8aeSRafael J. Wysocki 		return -EINVAL;
10850496c8aeSRafael J. Wysocki 
10860496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
10870496c8aeSRafael J. Wysocki }
10880496c8aeSRafael J. Wysocki 
10890496c8aeSRafael J. Wysocki /**
10900496c8aeSRafael J. Wysocki  * pm_genpd_resume - Resume of device in an I/O PM domain.
1091596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1092596ba34bSRafael J. Wysocki  *
1093596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
1094596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1095596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1096596ba34bSRafael J. Wysocki  */
1097596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
1098596ba34bSRafael J. Wysocki {
1099596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1100596ba34bSRafael J. Wysocki 
1101596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1102596ba34bSRafael J. Wysocki 
1103596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1104596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1105596ba34bSRafael J. Wysocki 		return -EINVAL;
1106596ba34bSRafael J. Wysocki 
1107d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
1108596ba34bSRafael J. Wysocki }
1109596ba34bSRafael J. Wysocki 
1110596ba34bSRafael J. Wysocki /**
11110496c8aeSRafael J. Wysocki  * pm_genpd_freeze - Freezing a device in an I/O PM domain.
1112596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1113596ba34bSRafael J. Wysocki  *
1114596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
1115596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1116596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1117596ba34bSRafael J. Wysocki  */
1118596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
1119596ba34bSRafael J. Wysocki {
1120596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1121596ba34bSRafael J. Wysocki 
1122596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1123596ba34bSRafael J. Wysocki 
1124596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1125596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1126596ba34bSRafael J. Wysocki 		return -EINVAL;
1127596ba34bSRafael J. Wysocki 
1128d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
1129596ba34bSRafael J. Wysocki }
1130596ba34bSRafael J. Wysocki 
1131596ba34bSRafael J. Wysocki /**
11320496c8aeSRafael J. Wysocki  * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
11330496c8aeSRafael J. Wysocki  * @dev: Device to freeze.
11340496c8aeSRafael J. Wysocki  *
11350496c8aeSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
11360496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
11370496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
11380496c8aeSRafael J. Wysocki  * devices.
11390496c8aeSRafael J. Wysocki  */
11400496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev)
11410496c8aeSRafael J. Wysocki {
11420496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
11430496c8aeSRafael J. Wysocki 
11440496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
11450496c8aeSRafael J. Wysocki 
11460496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
11470496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
11480496c8aeSRafael J. Wysocki 		return -EINVAL;
11490496c8aeSRafael J. Wysocki 
11500496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
11510496c8aeSRafael J. Wysocki }
11520496c8aeSRafael J. Wysocki 
11530496c8aeSRafael J. Wysocki /**
11540496c8aeSRafael J. Wysocki  * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1155596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1156596ba34bSRafael J. Wysocki  *
1157596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1158596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1159596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1160596ba34bSRafael J. Wysocki  * devices.
1161596ba34bSRafael J. Wysocki  */
1162596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
1163596ba34bSRafael J. Wysocki {
1164596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1165596ba34bSRafael J. Wysocki 
1166596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1167596ba34bSRafael J. Wysocki 
1168596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1169596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1170596ba34bSRafael J. Wysocki 		return -EINVAL;
1171596ba34bSRafael J. Wysocki 
1172dbf37414SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
1173596ba34bSRafael J. Wysocki }
1174596ba34bSRafael J. Wysocki 
1175596ba34bSRafael J. Wysocki /**
11760496c8aeSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1177596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1178596ba34bSRafael J. Wysocki  *
11790496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
11800496c8aeSRafael J. Wysocki  * before the system transition.
1181596ba34bSRafael J. Wysocki  */
1182596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
1183596ba34bSRafael J. Wysocki {
1184596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1185596ba34bSRafael J. Wysocki 
1186596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1187596ba34bSRafael J. Wysocki 
1188596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1189596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1190596ba34bSRafael J. Wysocki 		return -EINVAL;
1191596ba34bSRafael J. Wysocki 
1192dbf37414SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
11930496c8aeSRafael J. Wysocki }
1194596ba34bSRafael J. Wysocki 
11950496c8aeSRafael J. Wysocki /**
11960496c8aeSRafael J. Wysocki  * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
11970496c8aeSRafael J. Wysocki  * @dev: Device to thaw.
11980496c8aeSRafael J. Wysocki  *
11990496c8aeSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
12000496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
12010496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
12020496c8aeSRafael J. Wysocki  * devices.
12030496c8aeSRafael J. Wysocki  */
12040496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev)
12050496c8aeSRafael J. Wysocki {
12060496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1207596ba34bSRafael J. Wysocki 
12080496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
12090496c8aeSRafael J. Wysocki 
12100496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
12110496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
12120496c8aeSRafael J. Wysocki 		return -EINVAL;
12130496c8aeSRafael J. Wysocki 
12140496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
1215596ba34bSRafael J. Wysocki }
1216596ba34bSRafael J. Wysocki 
1217596ba34bSRafael J. Wysocki /**
1218596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1219596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1220596ba34bSRafael J. Wysocki  *
1221596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1222596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1223596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1224596ba34bSRafael J. Wysocki  */
1225596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1226596ba34bSRafael J. Wysocki {
1227596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1228596ba34bSRafael J. Wysocki 
1229596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1230596ba34bSRafael J. Wysocki 
1231596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1232596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1233596ba34bSRafael J. Wysocki 		return -EINVAL;
1234596ba34bSRafael J. Wysocki 
1235d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1236596ba34bSRafael J. Wysocki }
1237596ba34bSRafael J. Wysocki 
1238596ba34bSRafael J. Wysocki /**
12390496c8aeSRafael J. Wysocki  * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1240596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1241596ba34bSRafael J. Wysocki  *
12420496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
12430496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1244596ba34bSRafael J. Wysocki  */
1245596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1246596ba34bSRafael J. Wysocki {
1247596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1248596ba34bSRafael J. Wysocki 
1249596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1250596ba34bSRafael J. Wysocki 
1251596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1252596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1253596ba34bSRafael J. Wysocki 		return -EINVAL;
1254596ba34bSRafael J. Wysocki 
1255596ba34bSRafael J. Wysocki 	/*
1256596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1257596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1258596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
125965533bbfSRafael J. Wysocki 	 *
126065533bbfSRafael J. Wysocki 	 * At this point suspended_count == 0 means we are being run for the
126165533bbfSRafael J. Wysocki 	 * first time for the given domain in the present cycle.
126265533bbfSRafael J. Wysocki 	 */
126365533bbfSRafael J. Wysocki 	if (genpd->suspended_count++ == 0) {
126465533bbfSRafael J. Wysocki 		/*
126565533bbfSRafael J. Wysocki 		 * The boot kernel might put the domain into arbitrary state,
1266802d8b49SRafael J. Wysocki 		 * so make it appear as powered off to pm_genpd_sync_poweron(),
1267802d8b49SRafael J. Wysocki 		 * so that it tries to power it on in case it was really off.
1268596ba34bSRafael J. Wysocki 		 */
126917b75ecaSRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1270596ba34bSRafael J. Wysocki 		if (genpd->suspend_power_off) {
1271596ba34bSRafael J. Wysocki 			/*
127265533bbfSRafael J. Wysocki 			 * If the domain was off before the hibernation, make
127365533bbfSRafael J. Wysocki 			 * sure it will be off going forward.
1274596ba34bSRafael J. Wysocki 			 */
1275596ba34bSRafael J. Wysocki 			if (genpd->power_off)
1276596ba34bSRafael J. Wysocki 				genpd->power_off(genpd);
127765533bbfSRafael J. Wysocki 
1278596ba34bSRafael J. Wysocki 			return 0;
1279596ba34bSRafael J. Wysocki 		}
128065533bbfSRafael J. Wysocki 	}
1281596ba34bSRafael J. Wysocki 
128218dd2eceSRafael J. Wysocki 	if (genpd->suspend_power_off)
128318dd2eceSRafael J. Wysocki 		return 0;
128418dd2eceSRafael J. Wysocki 
1285802d8b49SRafael J. Wysocki 	pm_genpd_sync_poweron(genpd);
1286596ba34bSRafael J. Wysocki 
1287dbf37414SRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1288596ba34bSRafael J. Wysocki }
1289596ba34bSRafael J. Wysocki 
1290596ba34bSRafael J. Wysocki /**
1291596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1292596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1293596ba34bSRafael J. Wysocki  *
1294596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1295596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1296596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1297596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1298596ba34bSRafael J. Wysocki  */
1299596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1300596ba34bSRafael J. Wysocki {
1301596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1302596ba34bSRafael J. Wysocki 	bool run_complete;
1303596ba34bSRafael J. Wysocki 
1304596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1305596ba34bSRafael J. Wysocki 
1306596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1307596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1308596ba34bSRafael J. Wysocki 		return;
1309596ba34bSRafael J. Wysocki 
1310596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1311596ba34bSRafael J. Wysocki 
1312596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1313596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1314596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1315596ba34bSRafael J. Wysocki 
1316596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1317596ba34bSRafael J. Wysocki 
1318596ba34bSRafael J. Wysocki 	if (run_complete) {
1319596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
13206f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1321596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
13226f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1323596ba34bSRafael J. Wysocki 	}
1324596ba34bSRafael J. Wysocki }
1325596ba34bSRafael J. Wysocki 
132677f827deSRafael J. Wysocki /**
132777f827deSRafael J. Wysocki  * pm_genpd_syscore_switch - Switch power during system core suspend or resume.
132877f827deSRafael J. Wysocki  * @dev: Device that normally is marked as "always on" to switch power for.
132977f827deSRafael J. Wysocki  *
133077f827deSRafael J. Wysocki  * This routine may only be called during the system core (syscore) suspend or
133177f827deSRafael J. Wysocki  * resume phase for devices whose "always on" flags are set.
133277f827deSRafael J. Wysocki  */
133377f827deSRafael J. Wysocki void pm_genpd_syscore_switch(struct device *dev, bool suspend)
133477f827deSRafael J. Wysocki {
133577f827deSRafael J. Wysocki 	struct generic_pm_domain *genpd;
133677f827deSRafael J. Wysocki 
133777f827deSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
133877f827deSRafael J. Wysocki 	if (!pm_genpd_present(genpd))
133977f827deSRafael J. Wysocki 		return;
134077f827deSRafael J. Wysocki 
134177f827deSRafael J. Wysocki 	if (suspend) {
134277f827deSRafael J. Wysocki 		genpd->suspended_count++;
134377f827deSRafael J. Wysocki 		pm_genpd_sync_poweroff(genpd);
134477f827deSRafael J. Wysocki 	} else {
134577f827deSRafael J. Wysocki 		pm_genpd_sync_poweron(genpd);
134677f827deSRafael J. Wysocki 		genpd->suspended_count--;
134777f827deSRafael J. Wysocki 	}
134877f827deSRafael J. Wysocki }
134977f827deSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch);
135077f827deSRafael J. Wysocki 
1351596ba34bSRafael J. Wysocki #else
1352596ba34bSRafael J. Wysocki 
1353596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1354596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
13550496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late		NULL
1356596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
13570496c8aeSRafael J. Wysocki #define pm_genpd_resume_early		NULL
1358596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1359596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1360596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
13610496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late		NULL
1362596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
13630496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early		NULL
1364596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1365596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1366596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1367596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1368596ba34bSRafael J. Wysocki 
1369596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1370596ba34bSRafael J. Wysocki 
13711d5fcfecSRafael J. Wysocki static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev)
13721d5fcfecSRafael J. Wysocki {
13731d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
13741d5fcfecSRafael J. Wysocki 
13751d5fcfecSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
13761d5fcfecSRafael J. Wysocki 	if (!gpd_data)
13771d5fcfecSRafael J. Wysocki 		return NULL;
13781d5fcfecSRafael J. Wysocki 
13791d5fcfecSRafael J. Wysocki 	mutex_init(&gpd_data->lock);
13801d5fcfecSRafael J. Wysocki 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
13811d5fcfecSRafael J. Wysocki 	dev_pm_qos_add_notifier(dev, &gpd_data->nb);
13821d5fcfecSRafael J. Wysocki 	return gpd_data;
13831d5fcfecSRafael J. Wysocki }
13841d5fcfecSRafael J. Wysocki 
13851d5fcfecSRafael J. Wysocki static void __pm_genpd_free_dev_data(struct device *dev,
13861d5fcfecSRafael J. Wysocki 				     struct generic_pm_domain_data *gpd_data)
13871d5fcfecSRafael J. Wysocki {
13881d5fcfecSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
13891d5fcfecSRafael J. Wysocki 	kfree(gpd_data);
13901d5fcfecSRafael J. Wysocki }
13911d5fcfecSRafael J. Wysocki 
1392f721889fSRafael J. Wysocki /**
1393b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1394f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1395f721889fSRafael J. Wysocki  * @dev: Device to be added.
1396b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1397f721889fSRafael J. Wysocki  */
1398b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1399b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1400f721889fSRafael J. Wysocki {
14011d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
14024605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1403f721889fSRafael J. Wysocki 	int ret = 0;
1404f721889fSRafael J. Wysocki 
1405f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1406f721889fSRafael J. Wysocki 
1407f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1408f721889fSRafael J. Wysocki 		return -EINVAL;
1409f721889fSRafael J. Wysocki 
14101d5fcfecSRafael J. Wysocki 	gpd_data_new = __pm_genpd_alloc_dev_data(dev);
14111d5fcfecSRafael J. Wysocki 	if (!gpd_data_new)
14126ff7bb0dSRafael J. Wysocki 		return -ENOMEM;
14136ff7bb0dSRafael J. Wysocki 
141417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1415f721889fSRafael J. Wysocki 
1416596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1417596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1418596ba34bSRafael J. Wysocki 		goto out;
1419596ba34bSRafael J. Wysocki 	}
1420596ba34bSRafael J. Wysocki 
14214605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
14224605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1423f721889fSRafael J. Wysocki 			ret = -EINVAL;
1424f721889fSRafael J. Wysocki 			goto out;
1425f721889fSRafael J. Wysocki 		}
1426f721889fSRafael J. Wysocki 
14271d5fcfecSRafael J. Wysocki 	ret = dev_pm_get_subsys_data(dev);
14281d5fcfecSRafael J. Wysocki 	if (ret)
14291d5fcfecSRafael J. Wysocki 		goto out;
14301d5fcfecSRafael J. Wysocki 
1431596ba34bSRafael J. Wysocki 	genpd->device_count++;
14326ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1433f721889fSRafael J. Wysocki 
14346ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
14351d5fcfecSRafael J. Wysocki 
14366ff7bb0dSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
14371d5fcfecSRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
14381d5fcfecSRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
14391d5fcfecSRafael J. Wysocki 	} else {
14401d5fcfecSRafael J. Wysocki 		gpd_data = gpd_data_new;
1441cd0ea672SRafael J. Wysocki 		dev->power.subsys_data->domain_data = &gpd_data->base;
14421d5fcfecSRafael J. Wysocki 	}
14431d5fcfecSRafael J. Wysocki 	gpd_data->refcount++;
1444b02c999aSRafael J. Wysocki 	if (td)
1445b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1446f721889fSRafael J. Wysocki 
14471d5fcfecSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
14481d5fcfecSRafael J. Wysocki 
14491d5fcfecSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
14501d5fcfecSRafael J. Wysocki 	gpd_data->base.dev = dev;
14511d5fcfecSRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
14521d5fcfecSRafael J. Wysocki 	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
14536ff7bb0dSRafael J. Wysocki 	gpd_data->td.constraint_changed = true;
14546ff7bb0dSRafael J. Wysocki 	gpd_data->td.effective_constraint_ns = -1;
14556ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
14566ff7bb0dSRafael J. Wysocki 
1457f721889fSRafael J. Wysocki  out:
145817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1459f721889fSRafael J. Wysocki 
14601d5fcfecSRafael J. Wysocki 	if (gpd_data != gpd_data_new)
14611d5fcfecSRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data_new);
14621d5fcfecSRafael J. Wysocki 
1463f721889fSRafael J. Wysocki 	return ret;
1464f721889fSRafael J. Wysocki }
1465f721889fSRafael J. Wysocki 
1466f721889fSRafael J. Wysocki /**
1467c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1468c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1469c8aa130bSThomas Abraham  *   the device is added to.
1470c8aa130bSThomas Abraham  * @dev: Device to be added.
1471c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1472c8aa130bSThomas Abraham  */
1473c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1474c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1475c8aa130bSThomas Abraham {
1476c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1477c8aa130bSThomas Abraham 
1478c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1479c8aa130bSThomas Abraham 
1480c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1481c8aa130bSThomas Abraham 		return -EINVAL;
1482c8aa130bSThomas Abraham 
1483c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1484c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1485c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1486c8aa130bSThomas Abraham 			genpd = gpd;
1487c8aa130bSThomas Abraham 			break;
1488c8aa130bSThomas Abraham 		}
1489c8aa130bSThomas Abraham 	}
1490c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1491c8aa130bSThomas Abraham 
1492c8aa130bSThomas Abraham 	if (!genpd)
1493c8aa130bSThomas Abraham 		return -EINVAL;
1494c8aa130bSThomas Abraham 
1495c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1496c8aa130bSThomas Abraham }
1497c8aa130bSThomas Abraham 
1498b5abb085SRafael J. Wysocki 
1499b5abb085SRafael J. Wysocki /**
1500b5abb085SRafael J. Wysocki  * __pm_genpd_name_add_device - Find I/O PM domain and add a device to it.
1501b5abb085SRafael J. Wysocki  * @domain_name: Name of the PM domain to add the device to.
1502b5abb085SRafael J. Wysocki  * @dev: Device to be added.
1503b5abb085SRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1504b5abb085SRafael J. Wysocki  */
1505b5abb085SRafael J. Wysocki int __pm_genpd_name_add_device(const char *domain_name, struct device *dev,
1506b5abb085SRafael J. Wysocki 			       struct gpd_timing_data *td)
1507b5abb085SRafael J. Wysocki {
15088bc0251dSRafael J. Wysocki 	return __pm_genpd_add_device(pm_genpd_lookup_name(domain_name), dev, td);
1509b5abb085SRafael J. Wysocki }
1510b5abb085SRafael J. Wysocki 
1511c8aa130bSThomas Abraham /**
1512f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1513f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1514f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1515f721889fSRafael J. Wysocki  */
1516f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1517f721889fSRafael J. Wysocki 			   struct device *dev)
1518f721889fSRafael J. Wysocki {
15196ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
15204605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
15211d5fcfecSRafael J. Wysocki 	bool remove = false;
1522efa69025SRafael J. Wysocki 	int ret = 0;
1523f721889fSRafael J. Wysocki 
1524f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1525f721889fSRafael J. Wysocki 
1526efa69025SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
1527efa69025SRafael J. Wysocki 	    ||  IS_ERR_OR_NULL(dev->pm_domain)
1528efa69025SRafael J. Wysocki 	    ||  pd_to_genpd(dev->pm_domain) != genpd)
1529f721889fSRafael J. Wysocki 		return -EINVAL;
1530f721889fSRafael J. Wysocki 
153117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1532f721889fSRafael J. Wysocki 
1533596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1534596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1535596ba34bSRafael J. Wysocki 		goto out;
1536596ba34bSRafael J. Wysocki 	}
1537596ba34bSRafael J. Wysocki 
15386ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
15396ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
15406ff7bb0dSRafael J. Wysocki 
15416ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
15421d5fcfecSRafael J. Wysocki 
1543f721889fSRafael J. Wysocki 	dev->pm_domain = NULL;
1544efa69025SRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1545efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
15461d5fcfecSRafael J. Wysocki 	gpd_data = to_gpd_data(pdd);
15471d5fcfecSRafael J. Wysocki 	if (--gpd_data->refcount == 0) {
1548efa69025SRafael J. Wysocki 		dev->power.subsys_data->domain_data = NULL;
15491d5fcfecSRafael J. Wysocki 		remove = true;
15501d5fcfecSRafael J. Wysocki 	}
15511d5fcfecSRafael J. Wysocki 
15526ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1553f721889fSRafael J. Wysocki 
15546ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
15556ff7bb0dSRafael J. Wysocki 	pdd->dev = NULL;
15566ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
15576ff7bb0dSRafael J. Wysocki 
15586ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
15596ff7bb0dSRafael J. Wysocki 
15606ff7bb0dSRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
15611d5fcfecSRafael J. Wysocki 	if (remove)
15621d5fcfecSRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data);
15631d5fcfecSRafael J. Wysocki 
15646ff7bb0dSRafael J. Wysocki 	return 0;
1565f721889fSRafael J. Wysocki 
1566596ba34bSRafael J. Wysocki  out:
156717b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1568f721889fSRafael J. Wysocki 
1569f721889fSRafael J. Wysocki 	return ret;
1570f721889fSRafael J. Wysocki }
1571f721889fSRafael J. Wysocki 
1572f721889fSRafael J. Wysocki /**
1573ca1d72f0SRafael J. Wysocki  * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
1574ca1d72f0SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
1575ca1d72f0SRafael J. Wysocki  * @val: The new value of the device's "need restore" flag.
1576ca1d72f0SRafael J. Wysocki  */
1577ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val)
1578ca1d72f0SRafael J. Wysocki {
1579ca1d72f0SRafael J. Wysocki 	struct pm_subsys_data *psd;
1580ca1d72f0SRafael J. Wysocki 	unsigned long flags;
1581ca1d72f0SRafael J. Wysocki 
1582ca1d72f0SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
1583ca1d72f0SRafael J. Wysocki 
1584ca1d72f0SRafael J. Wysocki 	psd = dev_to_psd(dev);
1585ca1d72f0SRafael J. Wysocki 	if (psd && psd->domain_data)
1586ca1d72f0SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->need_restore = val;
1587ca1d72f0SRafael J. Wysocki 
1588ca1d72f0SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1589ca1d72f0SRafael J. Wysocki }
1590ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
1591ca1d72f0SRafael J. Wysocki 
1592ca1d72f0SRafael J. Wysocki /**
1593f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1594f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1595bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1596f721889fSRafael J. Wysocki  */
1597f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1598bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1599f721889fSRafael J. Wysocki {
16005063ce15SRafael J. Wysocki 	struct gpd_link *link;
1601f721889fSRafael J. Wysocki 	int ret = 0;
1602f721889fSRafael J. Wysocki 
1603fb7268beSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)
1604fb7268beSRafael J. Wysocki 	    || genpd == subdomain)
1605f721889fSRafael J. Wysocki 		return -EINVAL;
1606f721889fSRafael J. Wysocki 
160717b75ecaSRafael J. Wysocki  start:
160817b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1609bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1610f721889fSRafael J. Wysocki 
1611bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1612bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1613bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
161417b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
161517b75ecaSRafael J. Wysocki 		goto start;
161617b75ecaSRafael J. Wysocki 	}
161717b75ecaSRafael J. Wysocki 
161817b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1619bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1620f721889fSRafael J. Wysocki 		ret = -EINVAL;
1621f721889fSRafael J. Wysocki 		goto out;
1622f721889fSRafael J. Wysocki 	}
1623f721889fSRafael J. Wysocki 
16244fcac10dSHuang Ying 	list_for_each_entry(link, &genpd->master_links, master_node) {
1625bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1626f721889fSRafael J. Wysocki 			ret = -EINVAL;
1627f721889fSRafael J. Wysocki 			goto out;
1628f721889fSRafael J. Wysocki 		}
1629f721889fSRafael J. Wysocki 	}
1630f721889fSRafael J. Wysocki 
16315063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
16325063ce15SRafael J. Wysocki 	if (!link) {
16335063ce15SRafael J. Wysocki 		ret = -ENOMEM;
16345063ce15SRafael J. Wysocki 		goto out;
16355063ce15SRafael J. Wysocki 	}
16365063ce15SRafael J. Wysocki 	link->master = genpd;
16375063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1638bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1639bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1640bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1641c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1642f721889fSRafael J. Wysocki 
1643f721889fSRafael J. Wysocki  out:
1644bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
164517b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1646f721889fSRafael J. Wysocki 
1647f721889fSRafael J. Wysocki 	return ret;
1648f721889fSRafael J. Wysocki }
1649f721889fSRafael J. Wysocki 
1650f721889fSRafael J. Wysocki /**
1651fb7268beSRafael J. Wysocki  * pm_genpd_add_subdomain_names - Add a subdomain to an I/O PM domain.
1652fb7268beSRafael J. Wysocki  * @master_name: Name of the master PM domain to add the subdomain to.
1653fb7268beSRafael J. Wysocki  * @subdomain_name: Name of the subdomain to be added.
1654fb7268beSRafael J. Wysocki  */
1655fb7268beSRafael J. Wysocki int pm_genpd_add_subdomain_names(const char *master_name,
1656fb7268beSRafael J. Wysocki 				 const char *subdomain_name)
1657fb7268beSRafael J. Wysocki {
1658fb7268beSRafael J. Wysocki 	struct generic_pm_domain *master = NULL, *subdomain = NULL, *gpd;
1659fb7268beSRafael J. Wysocki 
1660fb7268beSRafael J. Wysocki 	if (IS_ERR_OR_NULL(master_name) || IS_ERR_OR_NULL(subdomain_name))
1661fb7268beSRafael J. Wysocki 		return -EINVAL;
1662fb7268beSRafael J. Wysocki 
1663fb7268beSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
1664fb7268beSRafael J. Wysocki 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1665fb7268beSRafael J. Wysocki 		if (!master && !strcmp(gpd->name, master_name))
1666fb7268beSRafael J. Wysocki 			master = gpd;
1667fb7268beSRafael J. Wysocki 
1668fb7268beSRafael J. Wysocki 		if (!subdomain && !strcmp(gpd->name, subdomain_name))
1669fb7268beSRafael J. Wysocki 			subdomain = gpd;
1670fb7268beSRafael J. Wysocki 
1671fb7268beSRafael J. Wysocki 		if (master && subdomain)
1672fb7268beSRafael J. Wysocki 			break;
1673fb7268beSRafael J. Wysocki 	}
1674fb7268beSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
1675fb7268beSRafael J. Wysocki 
1676fb7268beSRafael J. Wysocki 	return pm_genpd_add_subdomain(master, subdomain);
1677fb7268beSRafael J. Wysocki }
1678fb7268beSRafael J. Wysocki 
1679fb7268beSRafael J. Wysocki /**
1680f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1681f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
16825063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1683f721889fSRafael J. Wysocki  */
1684f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
16855063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1686f721889fSRafael J. Wysocki {
16875063ce15SRafael J. Wysocki 	struct gpd_link *link;
1688f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1689f721889fSRafael J. Wysocki 
16905063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1691f721889fSRafael J. Wysocki 		return -EINVAL;
1692f721889fSRafael J. Wysocki 
169317b75ecaSRafael J. Wysocki  start:
169417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1695f721889fSRafael J. Wysocki 
16965063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
16975063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1698f721889fSRafael J. Wysocki 			continue;
1699f721889fSRafael J. Wysocki 
1700f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1701f721889fSRafael J. Wysocki 
170217b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
170317b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
170417b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
170517b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
170617b75ecaSRafael J. Wysocki 			goto start;
170717b75ecaSRafael J. Wysocki 		}
170817b75ecaSRafael J. Wysocki 
17095063ce15SRafael J. Wysocki 		list_del(&link->master_node);
17105063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
17115063ce15SRafael J. Wysocki 		kfree(link);
171217b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1713f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1714f721889fSRafael J. Wysocki 
1715f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1716f721889fSRafael J. Wysocki 
1717f721889fSRafael J. Wysocki 		ret = 0;
1718f721889fSRafael J. Wysocki 		break;
1719f721889fSRafael J. Wysocki 	}
1720f721889fSRafael J. Wysocki 
172117b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1722f721889fSRafael J. Wysocki 
1723f721889fSRafael J. Wysocki 	return ret;
1724f721889fSRafael J. Wysocki }
1725f721889fSRafael J. Wysocki 
1726f721889fSRafael J. Wysocki /**
1727d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1728d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1729d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1730b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
173162d44902SRafael J. Wysocki  *
173262d44902SRafael J. Wysocki  * Every call to this routine should be balanced with a call to
173362d44902SRafael J. Wysocki  * __pm_genpd_remove_callbacks() and they must not be nested.
1734d5e4cbfeSRafael J. Wysocki  */
1735b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1736b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1737d5e4cbfeSRafael J. Wysocki {
173862d44902SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
1739d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1740d5e4cbfeSRafael J. Wysocki 
174162d44902SRafael J. Wysocki 	if (!(dev && ops))
1742d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1743d5e4cbfeSRafael J. Wysocki 
174462d44902SRafael J. Wysocki 	gpd_data_new = __pm_genpd_alloc_dev_data(dev);
174562d44902SRafael J. Wysocki 	if (!gpd_data_new)
174662d44902SRafael J. Wysocki 		return -ENOMEM;
174762d44902SRafael J. Wysocki 
1748d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1749d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1750d5e4cbfeSRafael J. Wysocki 
175162d44902SRafael J. Wysocki 	ret = dev_pm_get_subsys_data(dev);
175262d44902SRafael J. Wysocki 	if (ret)
175362d44902SRafael J. Wysocki 		goto out;
1754d5e4cbfeSRafael J. Wysocki 
175562d44902SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
175662d44902SRafael J. Wysocki 
175762d44902SRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
175862d44902SRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
175962d44902SRafael J. Wysocki 	} else {
176062d44902SRafael J. Wysocki 		gpd_data = gpd_data_new;
176162d44902SRafael J. Wysocki 		dev->power.subsys_data->domain_data = &gpd_data->base;
176262d44902SRafael J. Wysocki 	}
176362d44902SRafael J. Wysocki 	gpd_data->refcount++;
1764d5e4cbfeSRafael J. Wysocki 	gpd_data->ops = *ops;
1765b02c999aSRafael J. Wysocki 	if (td)
1766b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1767d5e4cbfeSRafael J. Wysocki 
176862d44902SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
176962d44902SRafael J. Wysocki 
177062d44902SRafael J. Wysocki  out:
1771d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1772d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1773d5e4cbfeSRafael J. Wysocki 
177462d44902SRafael J. Wysocki 	if (gpd_data != gpd_data_new)
177562d44902SRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data_new);
177662d44902SRafael J. Wysocki 
1777d5e4cbfeSRafael J. Wysocki 	return ret;
1778d5e4cbfeSRafael J. Wysocki }
1779d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1780d5e4cbfeSRafael J. Wysocki 
1781d5e4cbfeSRafael J. Wysocki /**
1782b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1783d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1784b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
178562d44902SRafael J. Wysocki  *
178662d44902SRafael J. Wysocki  * This routine can only be called after pm_genpd_add_callbacks().
1787d5e4cbfeSRafael J. Wysocki  */
1788b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1789d5e4cbfeSRafael J. Wysocki {
179062d44902SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = NULL;
179162d44902SRafael J. Wysocki 	bool remove = false;
1792d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1793d5e4cbfeSRafael J. Wysocki 
1794d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1795d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1796d5e4cbfeSRafael J. Wysocki 
1797d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1798d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1799d5e4cbfeSRafael J. Wysocki 
180062d44902SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1801d5e4cbfeSRafael J. Wysocki 
180262d44902SRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
180362d44902SRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1804db79e53dSSachin Kamat 		gpd_data->ops = (struct gpd_dev_ops){ NULL };
1805b02c999aSRafael J. Wysocki 		if (clear_td)
1806b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
180762d44902SRafael J. Wysocki 
180862d44902SRafael J. Wysocki 		if (--gpd_data->refcount == 0) {
180962d44902SRafael J. Wysocki 			dev->power.subsys_data->domain_data = NULL;
181062d44902SRafael J. Wysocki 			remove = true;
181162d44902SRafael J. Wysocki 		}
1812d5e4cbfeSRafael J. Wysocki 	} else {
1813d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1814d5e4cbfeSRafael J. Wysocki 	}
1815d5e4cbfeSRafael J. Wysocki 
181662d44902SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
181762d44902SRafael J. Wysocki 
1818d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1819d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1820d5e4cbfeSRafael J. Wysocki 
182162d44902SRafael J. Wysocki 	if (ret)
1822d5e4cbfeSRafael J. Wysocki 		return ret;
182362d44902SRafael J. Wysocki 
182462d44902SRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
182562d44902SRafael J. Wysocki 	if (remove)
182662d44902SRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data);
182762d44902SRafael J. Wysocki 
182862d44902SRafael J. Wysocki 	return 0;
1829d5e4cbfeSRafael J. Wysocki }
1830b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1831d5e4cbfeSRafael J. Wysocki 
183240114447SRafael J. Wysocki /**
183340114447SRafael J. Wysocki  * pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle.
183440114447SRafael J. Wysocki  * @genpd: PM domain to be connected with cpuidle.
183540114447SRafael J. Wysocki  * @state: cpuidle state this domain can disable/enable.
183640114447SRafael J. Wysocki  *
183740114447SRafael J. Wysocki  * Make a PM domain behave as though it contained a CPU core, that is, instead
183840114447SRafael J. Wysocki  * of calling its power down routine it will enable the given cpuidle state so
183940114447SRafael J. Wysocki  * that the cpuidle subsystem can power it down (if possible and desirable).
184040114447SRafael J. Wysocki  */
184140114447SRafael J. Wysocki int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
1842cbc9ef02SRafael J. Wysocki {
1843cbc9ef02SRafael J. Wysocki 	struct cpuidle_driver *cpuidle_drv;
1844cbc9ef02SRafael J. Wysocki 	struct gpd_cpu_data *cpu_data;
1845cbc9ef02SRafael J. Wysocki 	struct cpuidle_state *idle_state;
1846cbc9ef02SRafael J. Wysocki 	int ret = 0;
1847cbc9ef02SRafael J. Wysocki 
1848cbc9ef02SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || state < 0)
1849cbc9ef02SRafael J. Wysocki 		return -EINVAL;
1850cbc9ef02SRafael J. Wysocki 
1851cbc9ef02SRafael J. Wysocki 	genpd_acquire_lock(genpd);
1852cbc9ef02SRafael J. Wysocki 
1853cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
1854cbc9ef02SRafael J. Wysocki 		ret = -EEXIST;
1855cbc9ef02SRafael J. Wysocki 		goto out;
1856cbc9ef02SRafael J. Wysocki 	}
1857cbc9ef02SRafael J. Wysocki 	cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL);
1858cbc9ef02SRafael J. Wysocki 	if (!cpu_data) {
1859cbc9ef02SRafael J. Wysocki 		ret = -ENOMEM;
1860cbc9ef02SRafael J. Wysocki 		goto out;
1861cbc9ef02SRafael J. Wysocki 	}
1862cbc9ef02SRafael J. Wysocki 	cpuidle_drv = cpuidle_driver_ref();
1863cbc9ef02SRafael J. Wysocki 	if (!cpuidle_drv) {
1864cbc9ef02SRafael J. Wysocki 		ret = -ENODEV;
1865cbc9ef02SRafael J. Wysocki 		goto out;
1866cbc9ef02SRafael J. Wysocki 	}
1867cbc9ef02SRafael J. Wysocki 	if (cpuidle_drv->state_count <= state) {
1868cbc9ef02SRafael J. Wysocki 		ret = -EINVAL;
1869cbc9ef02SRafael J. Wysocki 		goto err;
1870cbc9ef02SRafael J. Wysocki 	}
1871cbc9ef02SRafael J. Wysocki 	idle_state = &cpuidle_drv->states[state];
1872cbc9ef02SRafael J. Wysocki 	if (!idle_state->disabled) {
1873cbc9ef02SRafael J. Wysocki 		ret = -EAGAIN;
1874cbc9ef02SRafael J. Wysocki 		goto err;
1875cbc9ef02SRafael J. Wysocki 	}
1876cbc9ef02SRafael J. Wysocki 	cpu_data->idle_state = idle_state;
1877cbc9ef02SRafael J. Wysocki 	cpu_data->saved_exit_latency = idle_state->exit_latency;
1878cbc9ef02SRafael J. Wysocki 	genpd->cpu_data = cpu_data;
1879cbc9ef02SRafael J. Wysocki 	genpd_recalc_cpu_exit_latency(genpd);
1880cbc9ef02SRafael J. Wysocki 
1881cbc9ef02SRafael J. Wysocki  out:
1882cbc9ef02SRafael J. Wysocki 	genpd_release_lock(genpd);
1883cbc9ef02SRafael J. Wysocki 	return ret;
1884cbc9ef02SRafael J. Wysocki 
1885cbc9ef02SRafael J. Wysocki  err:
1886cbc9ef02SRafael J. Wysocki 	cpuidle_driver_unref();
1887cbc9ef02SRafael J. Wysocki 	goto out;
1888cbc9ef02SRafael J. Wysocki }
1889cbc9ef02SRafael J. Wysocki 
189040114447SRafael J. Wysocki /**
189174a2799aSRafael J. Wysocki  * pm_genpd_name_attach_cpuidle - Find PM domain and connect cpuidle to it.
189274a2799aSRafael J. Wysocki  * @name: Name of the domain to connect to cpuidle.
189374a2799aSRafael J. Wysocki  * @state: cpuidle state this domain can manipulate.
189474a2799aSRafael J. Wysocki  */
189574a2799aSRafael J. Wysocki int pm_genpd_name_attach_cpuidle(const char *name, int state)
189674a2799aSRafael J. Wysocki {
189774a2799aSRafael J. Wysocki 	return pm_genpd_attach_cpuidle(pm_genpd_lookup_name(name), state);
189874a2799aSRafael J. Wysocki }
189974a2799aSRafael J. Wysocki 
190074a2799aSRafael J. Wysocki /**
190140114447SRafael J. Wysocki  * pm_genpd_detach_cpuidle - Remove the cpuidle connection from a PM domain.
190240114447SRafael J. Wysocki  * @genpd: PM domain to remove the cpuidle connection from.
190340114447SRafael J. Wysocki  *
190440114447SRafael J. Wysocki  * Remove the cpuidle connection set up by pm_genpd_attach_cpuidle() from the
190540114447SRafael J. Wysocki  * given PM domain.
190640114447SRafael J. Wysocki  */
190740114447SRafael J. Wysocki int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd)
1908cbc9ef02SRafael J. Wysocki {
1909cbc9ef02SRafael J. Wysocki 	struct gpd_cpu_data *cpu_data;
1910cbc9ef02SRafael J. Wysocki 	struct cpuidle_state *idle_state;
1911cbc9ef02SRafael J. Wysocki 	int ret = 0;
1912cbc9ef02SRafael J. Wysocki 
1913cbc9ef02SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1914cbc9ef02SRafael J. Wysocki 		return -EINVAL;
1915cbc9ef02SRafael J. Wysocki 
1916cbc9ef02SRafael J. Wysocki 	genpd_acquire_lock(genpd);
1917cbc9ef02SRafael J. Wysocki 
1918cbc9ef02SRafael J. Wysocki 	cpu_data = genpd->cpu_data;
1919cbc9ef02SRafael J. Wysocki 	if (!cpu_data) {
1920cbc9ef02SRafael J. Wysocki 		ret = -ENODEV;
1921cbc9ef02SRafael J. Wysocki 		goto out;
1922cbc9ef02SRafael J. Wysocki 	}
1923cbc9ef02SRafael J. Wysocki 	idle_state = cpu_data->idle_state;
1924cbc9ef02SRafael J. Wysocki 	if (!idle_state->disabled) {
1925cbc9ef02SRafael J. Wysocki 		ret = -EAGAIN;
1926cbc9ef02SRafael J. Wysocki 		goto out;
1927cbc9ef02SRafael J. Wysocki 	}
1928cbc9ef02SRafael J. Wysocki 	idle_state->exit_latency = cpu_data->saved_exit_latency;
1929cbc9ef02SRafael J. Wysocki 	cpuidle_driver_unref();
1930cbc9ef02SRafael J. Wysocki 	genpd->cpu_data = NULL;
1931cbc9ef02SRafael J. Wysocki 	kfree(cpu_data);
1932cbc9ef02SRafael J. Wysocki 
1933cbc9ef02SRafael J. Wysocki  out:
1934cbc9ef02SRafael J. Wysocki 	genpd_release_lock(genpd);
1935cbc9ef02SRafael J. Wysocki 	return ret;
1936cbc9ef02SRafael J. Wysocki }
1937cbc9ef02SRafael J. Wysocki 
193874a2799aSRafael J. Wysocki /**
193974a2799aSRafael J. Wysocki  * pm_genpd_name_detach_cpuidle - Find PM domain and disconnect cpuidle from it.
194074a2799aSRafael J. Wysocki  * @name: Name of the domain to disconnect cpuidle from.
194174a2799aSRafael J. Wysocki  */
194274a2799aSRafael J. Wysocki int pm_genpd_name_detach_cpuidle(const char *name)
194374a2799aSRafael J. Wysocki {
194474a2799aSRafael J. Wysocki 	return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name));
194574a2799aSRafael J. Wysocki }
194674a2799aSRafael J. Wysocki 
1947d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1948d23b9b00SRafael J. Wysocki 
1949d5e4cbfeSRafael J. Wysocki /**
1950ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1951ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1952ecf00475SRafael J. Wysocki  */
1953ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1954ecf00475SRafael J. Wysocki {
1955ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1956ecf00475SRafael J. Wysocki 
1957ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1958ecf00475SRafael J. Wysocki 	if (cb)
1959ecf00475SRafael J. Wysocki 		return cb(dev);
1960ecf00475SRafael J. Wysocki 
19610b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
19620b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_suspend;
19630b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
19640b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_suspend;
19650b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
19660b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_suspend;
19670b589741SRafael J. Wysocki 	else
19680b589741SRafael J. Wysocki 		cb = NULL;
1969ecf00475SRafael J. Wysocki 
19700b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
19710b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_suspend;
19720b589741SRafael J. Wysocki 
19730b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1974ecf00475SRafael J. Wysocki }
1975ecf00475SRafael J. Wysocki 
1976ecf00475SRafael J. Wysocki /**
1977ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1978ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1979ecf00475SRafael J. Wysocki  */
1980ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1981ecf00475SRafael J. Wysocki {
1982ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1983ecf00475SRafael J. Wysocki 
1984ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1985ecf00475SRafael J. Wysocki 	if (cb)
1986ecf00475SRafael J. Wysocki 		return cb(dev);
1987ecf00475SRafael J. Wysocki 
19880b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
19890b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_resume;
19900b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
19910b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_resume;
19920b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
19930b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_resume;
19940b589741SRafael J. Wysocki 	else
19950b589741SRafael J. Wysocki 		cb = NULL;
1996ecf00475SRafael J. Wysocki 
19970b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
19980b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_resume;
19990b589741SRafael J. Wysocki 
20000b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
2001ecf00475SRafael J. Wysocki }
2002ecf00475SRafael J. Wysocki 
20030f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
20040f1d6986SRafael J. Wysocki 
2005ecf00475SRafael J. Wysocki /**
2006d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
2007d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2008d23b9b00SRafael J. Wysocki  */
2009d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
2010d23b9b00SRafael J. Wysocki {
2011c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
2012d23b9b00SRafael J. Wysocki 
2013d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
2014d23b9b00SRafael J. Wysocki }
2015d23b9b00SRafael J. Wysocki 
2016d23b9b00SRafael J. Wysocki /**
2017d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
2018d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2019d23b9b00SRafael J. Wysocki  */
2020d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
2021d23b9b00SRafael J. Wysocki {
2022c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
2023d23b9b00SRafael J. Wysocki 
20240496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_late(dev);
2025d23b9b00SRafael J. Wysocki }
2026d23b9b00SRafael J. Wysocki 
2027d23b9b00SRafael J. Wysocki /**
2028d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
2029d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2030d23b9b00SRafael J. Wysocki  */
2031d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
2032d23b9b00SRafael J. Wysocki {
2033c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
2034d23b9b00SRafael J. Wysocki 
20350496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_early(dev);
2036d23b9b00SRafael J. Wysocki }
2037d23b9b00SRafael J. Wysocki 
2038d23b9b00SRafael J. Wysocki /**
2039d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
2040d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2041d23b9b00SRafael J. Wysocki  */
2042d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
2043d23b9b00SRafael J. Wysocki {
2044c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
2045d23b9b00SRafael J. Wysocki 
2046d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
2047d23b9b00SRafael J. Wysocki }
2048d23b9b00SRafael J. Wysocki 
2049d23b9b00SRafael J. Wysocki /**
2050d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
2051d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2052d23b9b00SRafael J. Wysocki  */
2053d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
2054d23b9b00SRafael J. Wysocki {
2055d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
2056d23b9b00SRafael J. Wysocki 
2057d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
2058d23b9b00SRafael J. Wysocki }
2059d23b9b00SRafael J. Wysocki 
2060d23b9b00SRafael J. Wysocki /**
2061d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
2062d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2063d23b9b00SRafael J. Wysocki  */
2064d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
2065d23b9b00SRafael J. Wysocki {
2066d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
2067d23b9b00SRafael J. Wysocki 
20680496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_late(dev);
2069d23b9b00SRafael J. Wysocki }
2070d23b9b00SRafael J. Wysocki 
2071d23b9b00SRafael J. Wysocki /**
2072d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
2073d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2074d23b9b00SRafael J. Wysocki  */
2075d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
2076d23b9b00SRafael J. Wysocki {
2077d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
2078d23b9b00SRafael J. Wysocki 
20790496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_early(dev);
2080d23b9b00SRafael J. Wysocki }
2081d23b9b00SRafael J. Wysocki 
2082d23b9b00SRafael J. Wysocki /**
2083d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
2084d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2085d23b9b00SRafael J. Wysocki  */
2086d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
2087d23b9b00SRafael J. Wysocki {
2088d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
2089d23b9b00SRafael J. Wysocki 
2090d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
2091d23b9b00SRafael J. Wysocki }
2092d23b9b00SRafael J. Wysocki 
20930f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
20940f1d6986SRafael J. Wysocki 
20950f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
20960f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
20970f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
20980f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
20990f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
21000f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
21010f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
21020f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
21030f1d6986SRafael J. Wysocki 
21040f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
21050f1d6986SRafael J. Wysocki 
2106d23b9b00SRafael J. Wysocki /**
2107f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
2108f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
2109f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
2110f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
2111f721889fSRafael J. Wysocki  */
2112f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
2113f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
2114f721889fSRafael J. Wysocki {
2115f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
2116f721889fSRafael J. Wysocki 		return;
2117f721889fSRafael J. Wysocki 
21185063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
21195063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
2120f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
2121f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
2122f721889fSRafael J. Wysocki 	genpd->gov = gov;
2123f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
2124f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
2125c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
212617b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
212717b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
2128c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
2129c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
2130596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
2131221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
21326ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
2133f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
2134f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
2135f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
2136596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
2137596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
21380496c8aeSRafael J. Wysocki 	genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
2139596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
2140596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
21410496c8aeSRafael J. Wysocki 	genpd->domain.ops.resume_early = pm_genpd_resume_early;
2142596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
2143596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
21440496c8aeSRafael J. Wysocki 	genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
2145596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
2146596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
21470496c8aeSRafael J. Wysocki 	genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
2148596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
2149d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
21500496c8aeSRafael J. Wysocki 	genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
2151d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
2152596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
21530496c8aeSRafael J. Wysocki 	genpd->domain.ops.restore_early = pm_genpd_resume_early;
2154d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
2155596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
2156ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
2157ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
2158c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
2159c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
2160c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
2161c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
2162d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
2163d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
2164d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
2165d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
21665125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
21675125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
21685125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
21695125bbf3SRafael J. Wysocki }
2170