xref: /openbmc/linux/drivers/base/power/domain.c (revision af939339)
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 	queue_work(pm_wq, &genpd->power_off_work);
43756375fd4SRafael J. Wysocki }
43856375fd4SRafael J. Wysocki 
43956375fd4SRafael J. Wysocki /**
440f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
441f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
442f721889fSRafael J. Wysocki  *
443f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
444f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
445f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
446f721889fSRafael J. Wysocki  */
447f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
44817b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
449f721889fSRafael J. Wysocki {
4504605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
4515063ce15SRafael J. Wysocki 	struct gpd_link *link;
452f721889fSRafael J. Wysocki 	unsigned int not_suspended;
453c6d22b37SRafael J. Wysocki 	int ret = 0;
454f721889fSRafael J. Wysocki 
455c6d22b37SRafael J. Wysocki  start:
456c6d22b37SRafael J. Wysocki 	/*
457c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
458c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
4595063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
460c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
4613f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
462c6d22b37SRafael J. Wysocki 	 */
4633f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
46417877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
4653f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
466f721889fSRafael J. Wysocki 		return 0;
467f721889fSRafael J. Wysocki 
468c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
469f721889fSRafael J. Wysocki 		return -EBUSY;
470f721889fSRafael J. Wysocki 
471f721889fSRafael J. Wysocki 	not_suspended = 0;
47234b1f762SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
47334b1f762SRafael J. Wysocki 		enum pm_qos_flags_status stat;
47434b1f762SRafael J. Wysocki 
47534b1f762SRafael J. Wysocki 		stat = dev_pm_qos_flags(pdd->dev,
47634b1f762SRafael J. Wysocki 					PM_QOS_FLAG_NO_POWER_OFF
47734b1f762SRafael J. Wysocki 						| PM_QOS_FLAG_REMOTE_WAKEUP);
47834b1f762SRafael J. Wysocki 		if (stat > PM_QOS_FLAGS_NONE)
47934b1f762SRafael J. Wysocki 			return -EBUSY;
48034b1f762SRafael J. Wysocki 
4810aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
482feb70af0SRafael J. Wysocki 		    || pdd->dev->power.irq_safe))
483f721889fSRafael J. Wysocki 			not_suspended++;
48434b1f762SRafael J. Wysocki 	}
485f721889fSRafael J. Wysocki 
486f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
487f721889fSRafael J. Wysocki 		return -EBUSY;
488f721889fSRafael J. Wysocki 
489c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
490c6d22b37SRafael J. Wysocki 		/*
491c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
492c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
493c6d22b37SRafael J. Wysocki 		 */
494c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
495c6d22b37SRafael J. Wysocki 		return 0;
496c6d22b37SRafael J. Wysocki 	}
497c6d22b37SRafael J. Wysocki 
498f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
499f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
500f721889fSRafael J. Wysocki 			return -EAGAIN;
501f721889fSRafael J. Wysocki 	}
502f721889fSRafael J. Wysocki 
50317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
504c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
50517b75ecaSRafael J. Wysocki 
5064605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
5073c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
5084605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
5093f241775SRafael J. Wysocki 
5103f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
5113f241775SRafael J. Wysocki 			goto out;
5123f241775SRafael J. Wysocki 
513697a7f37SRafael J. Wysocki 		if (ret) {
514697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
515697a7f37SRafael J. Wysocki 			goto out;
516697a7f37SRafael J. Wysocki 		}
517f721889fSRafael J. Wysocki 
518c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
519c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
520c6d22b37SRafael J. Wysocki 			goto start;
521c6d22b37SRafael J. Wysocki 		}
522c6d22b37SRafael J. Wysocki 	}
52317b75ecaSRafael J. Wysocki 
524cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
525cbc9ef02SRafael J. Wysocki 		/*
526cbc9ef02SRafael J. Wysocki 		 * If cpu_data is set, cpuidle should turn the domain off when
527cbc9ef02SRafael J. Wysocki 		 * the CPU in it is idle.  In that case we don't decrement the
528cbc9ef02SRafael J. Wysocki 		 * subdomain counts of the master domains, so that power is not
529cbc9ef02SRafael J. Wysocki 		 * removed from the current domain prematurely as a result of
530cbc9ef02SRafael J. Wysocki 		 * cutting off the masters' power.
531cbc9ef02SRafael J. Wysocki 		 */
532cbc9ef02SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
533cbc9ef02SRafael J. Wysocki 		cpuidle_pause_and_lock();
534cbc9ef02SRafael J. Wysocki 		genpd->cpu_data->idle_state->disabled = false;
535cbc9ef02SRafael J. Wysocki 		cpuidle_resume_and_unlock();
536cbc9ef02SRafael J. Wysocki 		goto out;
537cbc9ef02SRafael J. Wysocki 	}
538cbc9ef02SRafael J. Wysocki 
5393c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
5400140d8bdSRafael J. Wysocki 		ktime_t time_start;
5410140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
5420140d8bdSRafael J. Wysocki 
5433c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
5443c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
545c6d22b37SRafael J. Wysocki 			goto out;
546c6d22b37SRafael J. Wysocki 		}
54717b75ecaSRafael J. Wysocki 
5480140d8bdSRafael J. Wysocki 		time_start = ktime_get();
5490140d8bdSRafael J. Wysocki 
5503c07cbc4SRafael J. Wysocki 		/*
5515063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
5525063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
5533c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
5543c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
5553c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
5563c07cbc4SRafael J. Wysocki 		 * happen very often).
5573c07cbc4SRafael J. Wysocki 		 */
558d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
559d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
560d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
561d2805402SRafael J. Wysocki 			goto out;
562d2805402SRafael J. Wysocki 		}
5630140d8bdSRafael J. Wysocki 
5640140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
565e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_off_latency_ns) {
5660140d8bdSRafael J. Wysocki 			genpd->power_off_latency_ns = elapsed_ns;
5676ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
568e84b2c20SRafael J. Wysocki 			if (genpd->name)
569e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-off latency exceeded, "
570e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
571e84b2c20SRafael J. Wysocki 					elapsed_ns);
572e84b2c20SRafael J. Wysocki 		}
573d2805402SRafael J. Wysocki 	}
574f721889fSRafael J. Wysocki 
57517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
576221e9b58SRafael J. Wysocki 
5775063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
5785063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
5795063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
5805063ce15SRafael J. Wysocki 	}
58117b75ecaSRafael J. Wysocki 
582c6d22b37SRafael J. Wysocki  out:
583c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
584c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
585c6d22b37SRafael J. Wysocki 	return ret;
586f721889fSRafael J. Wysocki }
587f721889fSRafael J. Wysocki 
588f721889fSRafael J. Wysocki /**
589f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
590f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
591f721889fSRafael J. Wysocki  */
592f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
593f721889fSRafael J. Wysocki {
594f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
595f721889fSRafael J. Wysocki 
596f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
597f721889fSRafael J. Wysocki 
59817b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
599f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
60017b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
601f721889fSRafael J. Wysocki }
602f721889fSRafael J. Wysocki 
603f721889fSRafael J. Wysocki /**
604f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
605f721889fSRafael J. Wysocki  * @dev: Device to suspend.
606f721889fSRafael J. Wysocki  *
607f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
608f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
609f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
610f721889fSRafael J. Wysocki  */
611f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
612f721889fSRafael J. Wysocki {
613f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
614b02c999aSRafael J. Wysocki 	bool (*stop_ok)(struct device *__dev);
615d5e4cbfeSRafael J. Wysocki 	int ret;
616f721889fSRafael J. Wysocki 
617f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
618f721889fSRafael J. Wysocki 
6195248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
6205248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
621f721889fSRafael J. Wysocki 		return -EINVAL;
622f721889fSRafael J. Wysocki 
6230aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
6240aa2a221SRafael J. Wysocki 
625b02c999aSRafael J. Wysocki 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
626b02c999aSRafael J. Wysocki 	if (stop_ok && !stop_ok(dev))
627b02c999aSRafael J. Wysocki 		return -EBUSY;
628b02c999aSRafael J. Wysocki 
629d5e4cbfeSRafael J. Wysocki 	ret = genpd_stop_dev(genpd, dev);
630f721889fSRafael J. Wysocki 	if (ret)
63117b75ecaSRafael J. Wysocki 		return ret;
63217b75ecaSRafael J. Wysocki 
6330aa2a221SRafael J. Wysocki 	/*
6340aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
6350aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
6360aa2a221SRafael J. Wysocki 	 */
6370aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
6380aa2a221SRafael J. Wysocki 		return 0;
6390aa2a221SRafael J. Wysocki 
640c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
641f721889fSRafael J. Wysocki 	genpd->in_progress++;
642f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
643f721889fSRafael J. Wysocki 	genpd->in_progress--;
644c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
645f721889fSRafael J. Wysocki 
646f721889fSRafael J. Wysocki 	return 0;
647f721889fSRafael J. Wysocki }
648f721889fSRafael J. Wysocki 
649f721889fSRafael J. Wysocki /**
650f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
651f721889fSRafael J. Wysocki  * @dev: Device to resume.
652f721889fSRafael J. Wysocki  *
653f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
654f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
655f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
656f721889fSRafael J. Wysocki  */
657f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
658f721889fSRafael J. Wysocki {
659f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
660c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
661f721889fSRafael J. Wysocki 	int ret;
662f721889fSRafael J. Wysocki 
663f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
664f721889fSRafael J. Wysocki 
6655248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
6665248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
667f721889fSRafael J. Wysocki 		return -EINVAL;
668f721889fSRafael J. Wysocki 
6690aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
6700aa2a221SRafael J. Wysocki 
6710aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
6720aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
673e2e3e4e5SRafael J. Wysocki 		return genpd_start_dev_no_timing(genpd, dev);
6740aa2a221SRafael J. Wysocki 
675c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
6763f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
6773f241775SRafael J. Wysocki 	if (ret) {
6783f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
6793f241775SRafael J. Wysocki 		return ret;
6803f241775SRafael J. Wysocki 	}
68117b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
682c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
683c6d22b37SRafael J. Wysocki 	for (;;) {
684c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
685c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
686c6d22b37SRafael J. Wysocki 		/*
687c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
688c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
689c6d22b37SRafael J. Wysocki 		 * not wait.
690c6d22b37SRafael J. Wysocki 		 */
691c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
692c6d22b37SRafael J. Wysocki 			break;
693c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
694c6d22b37SRafael J. Wysocki 
695c6d22b37SRafael J. Wysocki 		schedule();
696c6d22b37SRafael J. Wysocki 
697c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
698c6d22b37SRafael J. Wysocki 	}
699c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
700cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
701c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
702c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
70317b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
704c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
70517b75ecaSRafael J. Wysocki 
706f721889fSRafael J. Wysocki 	return 0;
707f721889fSRafael J. Wysocki }
708f721889fSRafael J. Wysocki 
70917f2ae7fSRafael J. Wysocki /**
71017f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
71117f2ae7fSRafael J. Wysocki  */
71217f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
71317f2ae7fSRafael J. Wysocki {
71417f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
71517f2ae7fSRafael J. Wysocki 
71617f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
71717f2ae7fSRafael J. Wysocki 
71817f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
71917f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
72017f2ae7fSRafael J. Wysocki 
72117f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
72217f2ae7fSRafael J. Wysocki }
72317f2ae7fSRafael J. Wysocki 
724f721889fSRafael J. Wysocki #else
725f721889fSRafael J. Wysocki 
7266ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
7276ff7bb0dSRafael J. Wysocki 					    unsigned long val, void *ptr)
7286ff7bb0dSRafael J. Wysocki {
7296ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
7306ff7bb0dSRafael J. Wysocki }
7316ff7bb0dSRafael J. Wysocki 
732f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
733f721889fSRafael J. Wysocki 
734f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
735f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
736f721889fSRafael J. Wysocki 
737f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
738f721889fSRafael J. Wysocki 
739596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
740596ba34bSRafael J. Wysocki 
74177f827deSRafael J. Wysocki /**
74277f827deSRafael J. Wysocki  * pm_genpd_present - Check if the given PM domain has been initialized.
74377f827deSRafael J. Wysocki  * @genpd: PM domain to check.
74477f827deSRafael J. Wysocki  */
74577f827deSRafael J. Wysocki static bool pm_genpd_present(struct generic_pm_domain *genpd)
74677f827deSRafael J. Wysocki {
74777f827deSRafael J. Wysocki 	struct generic_pm_domain *gpd;
74877f827deSRafael J. Wysocki 
74977f827deSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
75077f827deSRafael J. Wysocki 		return false;
75177f827deSRafael J. Wysocki 
75277f827deSRafael J. Wysocki 	list_for_each_entry(gpd, &gpd_list, gpd_list_node)
75377f827deSRafael J. Wysocki 		if (gpd == genpd)
75477f827deSRafael J. Wysocki 			return true;
75577f827deSRafael J. Wysocki 
75677f827deSRafael J. Wysocki 	return false;
75777f827deSRafael J. Wysocki }
75877f827deSRafael J. Wysocki 
759d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
760d5e4cbfeSRafael J. Wysocki 				    struct device *dev)
761d5e4cbfeSRafael J. Wysocki {
762d5e4cbfeSRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
763d5e4cbfeSRafael J. Wysocki }
764d5e4cbfeSRafael J. Wysocki 
765d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
766d23b9b00SRafael J. Wysocki {
767d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
768d23b9b00SRafael J. Wysocki }
769d23b9b00SRafael J. Wysocki 
770d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
771d23b9b00SRafael J. Wysocki {
772d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
773d23b9b00SRafael J. Wysocki }
774d23b9b00SRafael J. Wysocki 
775d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
776d23b9b00SRafael J. Wysocki {
777d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
778d23b9b00SRafael J. Wysocki }
779d23b9b00SRafael J. Wysocki 
780d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
781d23b9b00SRafael J. Wysocki {
782d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
783d23b9b00SRafael J. Wysocki }
784d23b9b00SRafael J. Wysocki 
785d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
786d23b9b00SRafael J. Wysocki {
787d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
788d23b9b00SRafael J. Wysocki }
789d23b9b00SRafael J. Wysocki 
790d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
791d23b9b00SRafael J. Wysocki {
792d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
793d23b9b00SRafael J. Wysocki }
794d23b9b00SRafael J. Wysocki 
795d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
796d23b9b00SRafael J. Wysocki {
797d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
798d23b9b00SRafael J. Wysocki }
799d23b9b00SRafael J. Wysocki 
800d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
801d23b9b00SRafael J. Wysocki {
802d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
803d23b9b00SRafael J. Wysocki }
804d23b9b00SRafael J. Wysocki 
805596ba34bSRafael J. Wysocki /**
8065063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
807596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
808596ba34bSRafael J. Wysocki  *
809596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
8105063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
811596ba34bSRafael J. Wysocki  *
81277f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
81377f827deSRafael J. Wysocki  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
81477f827deSRafael J. Wysocki  * executed sequentially, so it is guaranteed that it will never run twice in
81577f827deSRafael J. Wysocki  * parallel).
816596ba34bSRafael J. Wysocki  */
817596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
818596ba34bSRafael J. Wysocki {
8195063ce15SRafael J. Wysocki 	struct gpd_link *link;
820596ba34bSRafael J. Wysocki 
82117b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
822596ba34bSRafael J. Wysocki 		return;
823596ba34bSRafael J. Wysocki 
824c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
825c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
826596ba34bSRafael J. Wysocki 		return;
827596ba34bSRafael J. Wysocki 
828596ba34bSRafael J. Wysocki 	if (genpd->power_off)
829596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
830596ba34bSRafael J. Wysocki 
83117b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
8325063ce15SRafael J. Wysocki 
8335063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
8345063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
8355063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
836596ba34bSRafael J. Wysocki 	}
837596ba34bSRafael J. Wysocki }
838596ba34bSRafael J. Wysocki 
839596ba34bSRafael J. Wysocki /**
840802d8b49SRafael J. Wysocki  * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
841802d8b49SRafael J. Wysocki  * @genpd: PM domain to power on.
842802d8b49SRafael J. Wysocki  *
84377f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
84477f827deSRafael J. Wysocki  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
84577f827deSRafael J. Wysocki  * executed sequentially, so it is guaranteed that it will never run twice in
84677f827deSRafael J. Wysocki  * parallel).
847802d8b49SRafael J. Wysocki  */
848802d8b49SRafael J. Wysocki static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
849802d8b49SRafael J. Wysocki {
850802d8b49SRafael J. Wysocki 	struct gpd_link *link;
851802d8b49SRafael J. Wysocki 
852802d8b49SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF)
853802d8b49SRafael J. Wysocki 		return;
854802d8b49SRafael J. Wysocki 
855802d8b49SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
856802d8b49SRafael J. Wysocki 		pm_genpd_sync_poweron(link->master);
857802d8b49SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
858802d8b49SRafael J. Wysocki 	}
859802d8b49SRafael J. Wysocki 
860802d8b49SRafael J. Wysocki 	if (genpd->power_on)
861802d8b49SRafael J. Wysocki 		genpd->power_on(genpd);
862802d8b49SRafael J. Wysocki 
863802d8b49SRafael J. Wysocki 	genpd->status = GPD_STATE_ACTIVE;
864802d8b49SRafael J. Wysocki }
865802d8b49SRafael J. Wysocki 
866802d8b49SRafael J. Wysocki /**
8674ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
8684ecd6e65SRafael J. Wysocki  * @dev: Device to check.
8694ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
8704ecd6e65SRafael J. Wysocki  *
8714ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
8724ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
8734ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
8744ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
8754ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
8764ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
8774ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
8784ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
8794ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
8804ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
8814ecd6e65SRafael J. Wysocki  */
8824ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
8834ecd6e65SRafael J. Wysocki {
8844ecd6e65SRafael J. Wysocki 	bool active_wakeup;
8854ecd6e65SRafael J. Wysocki 
8864ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
8874ecd6e65SRafael J. Wysocki 		return false;
8884ecd6e65SRafael J. Wysocki 
889d5e4cbfeSRafael J. Wysocki 	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
8904ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
8914ecd6e65SRafael J. Wysocki }
8924ecd6e65SRafael J. Wysocki 
8934ecd6e65SRafael J. Wysocki /**
894596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
895596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
896596ba34bSRafael J. Wysocki  *
897596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
898596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
899596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
900596ba34bSRafael J. Wysocki  * consisting of I/O devices.
901596ba34bSRafael J. Wysocki  */
902596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
903596ba34bSRafael J. Wysocki {
904596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
905b6c10c84SRafael J. Wysocki 	int ret;
906596ba34bSRafael J. Wysocki 
907596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
908596ba34bSRafael J. Wysocki 
909596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
910596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
911596ba34bSRafael J. Wysocki 		return -EINVAL;
912596ba34bSRafael J. Wysocki 
91317b75ecaSRafael J. Wysocki 	/*
91417b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
91517b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
91617b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
91717b75ecaSRafael J. Wysocki 	 */
91817b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
91917b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
92017b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
92117b75ecaSRafael J. Wysocki 
92217b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
92317b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
92417b75ecaSRafael J. Wysocki 		return -EBUSY;
92517b75ecaSRafael J. Wysocki 	}
92617b75ecaSRafael J. Wysocki 
9274ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
9284ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
9294ecd6e65SRafael J. Wysocki 
93017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
931596ba34bSRafael J. Wysocki 
93265533bbfSRafael J. Wysocki 	if (genpd->prepared_count++ == 0) {
93365533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
93417b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
93565533bbfSRafael J. Wysocki 	}
93617b75ecaSRafael J. Wysocki 
93717b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
938596ba34bSRafael J. Wysocki 
939596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
94017b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
941596ba34bSRafael J. Wysocki 		return 0;
942596ba34bSRafael J. Wysocki 	}
943596ba34bSRafael J. Wysocki 
944596ba34bSRafael J. Wysocki 	/*
94517b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
94617b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
947d5e4cbfeSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
94817b75ecaSRafael J. Wysocki 	 * to make it operational.
949596ba34bSRafael J. Wysocki 	 */
95017b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
951596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
952596ba34bSRafael J. Wysocki 
953b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
954b6c10c84SRafael J. Wysocki 	if (ret) {
955b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
956b6c10c84SRafael J. Wysocki 
957b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
958b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
959b6c10c84SRafael J. Wysocki 
960b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
96117b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
962b6c10c84SRafael J. Wysocki 	}
96317b75ecaSRafael J. Wysocki 
96417b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
965b6c10c84SRafael J. Wysocki 	return ret;
966596ba34bSRafael J. Wysocki }
967596ba34bSRafael J. Wysocki 
968596ba34bSRafael J. Wysocki /**
969596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
970596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
971596ba34bSRafael J. Wysocki  *
972596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
973596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
974596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
975596ba34bSRafael J. Wysocki  */
976596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
977596ba34bSRafael J. Wysocki {
978596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
979596ba34bSRafael J. Wysocki 
980596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
981596ba34bSRafael J. Wysocki 
982596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
983596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
984596ba34bSRafael J. Wysocki 		return -EINVAL;
985596ba34bSRafael J. Wysocki 
986d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
987596ba34bSRafael J. Wysocki }
988596ba34bSRafael J. Wysocki 
989596ba34bSRafael J. Wysocki /**
9900496c8aeSRafael J. Wysocki  * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
991596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
992596ba34bSRafael J. Wysocki  *
993596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
994596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
995596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
996596ba34bSRafael J. Wysocki  */
9970496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev)
998596ba34bSRafael J. Wysocki {
999596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1000596ba34bSRafael J. Wysocki 
1001596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1002596ba34bSRafael J. Wysocki 
1003596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1004596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1005596ba34bSRafael J. Wysocki 		return -EINVAL;
1006596ba34bSRafael J. Wysocki 
10070496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
10080496c8aeSRafael J. Wysocki }
1009596ba34bSRafael J. Wysocki 
10100496c8aeSRafael J. Wysocki /**
10110496c8aeSRafael J. Wysocki  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
10120496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
10130496c8aeSRafael J. Wysocki  *
10140496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
10150496c8aeSRafael J. Wysocki  * been stopped.
10160496c8aeSRafael J. Wysocki  */
10170496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
10180496c8aeSRafael J. Wysocki {
10190496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1020596ba34bSRafael J. Wysocki 
10210496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10220496c8aeSRafael J. Wysocki 
10230496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10240496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10250496c8aeSRafael J. Wysocki 		return -EINVAL;
10260496c8aeSRafael J. Wysocki 
1027dbf37414SRafael J. Wysocki 	if (genpd->suspend_power_off
10280496c8aeSRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
1029d4f2d87aSRafael J. Wysocki 		return 0;
1030d4f2d87aSRafael J. Wysocki 
1031d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
1032596ba34bSRafael J. Wysocki 
1033596ba34bSRafael J. Wysocki 	/*
1034596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1035596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1036596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1037596ba34bSRafael J. Wysocki 	 */
1038596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
1039596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
1040596ba34bSRafael J. Wysocki 
1041596ba34bSRafael J. Wysocki 	return 0;
1042596ba34bSRafael J. Wysocki }
1043596ba34bSRafael J. Wysocki 
1044596ba34bSRafael J. Wysocki /**
10450496c8aeSRafael J. Wysocki  * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
1046596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1047596ba34bSRafael J. Wysocki  *
10480496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
1049596ba34bSRafael J. Wysocki  */
1050596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
1051596ba34bSRafael J. Wysocki {
1052596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1053596ba34bSRafael J. Wysocki 
1054596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1055596ba34bSRafael J. Wysocki 
1056596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1057596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1058596ba34bSRafael J. Wysocki 		return -EINVAL;
1059596ba34bSRafael J. Wysocki 
1060dbf37414SRafael J. Wysocki 	if (genpd->suspend_power_off
1061cc85b207SRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
1062596ba34bSRafael J. Wysocki 		return 0;
1063596ba34bSRafael J. Wysocki 
1064596ba34bSRafael J. Wysocki 	/*
1065596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1066596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1067596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1068596ba34bSRafael J. Wysocki 	 */
1069802d8b49SRafael J. Wysocki 	pm_genpd_sync_poweron(genpd);
1070596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
1071596ba34bSRafael J. Wysocki 
10720496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1073596ba34bSRafael J. Wysocki }
1074596ba34bSRafael J. Wysocki 
1075596ba34bSRafael J. Wysocki /**
10760496c8aeSRafael J. Wysocki  * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
10770496c8aeSRafael J. Wysocki  * @dev: Device to resume.
10780496c8aeSRafael J. Wysocki  *
10790496c8aeSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
10800496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10810496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10820496c8aeSRafael J. Wysocki  * devices.
10830496c8aeSRafael J. Wysocki  */
10840496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev)
10850496c8aeSRafael J. Wysocki {
10860496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
10870496c8aeSRafael J. Wysocki 
10880496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10890496c8aeSRafael J. Wysocki 
10900496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10910496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10920496c8aeSRafael J. Wysocki 		return -EINVAL;
10930496c8aeSRafael J. Wysocki 
10940496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
10950496c8aeSRafael J. Wysocki }
10960496c8aeSRafael J. Wysocki 
10970496c8aeSRafael J. Wysocki /**
10980496c8aeSRafael J. Wysocki  * pm_genpd_resume - Resume of device in an I/O PM domain.
1099596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1100596ba34bSRafael J. Wysocki  *
1101596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
1102596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1103596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1104596ba34bSRafael J. Wysocki  */
1105596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
1106596ba34bSRafael J. Wysocki {
1107596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1108596ba34bSRafael J. Wysocki 
1109596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1110596ba34bSRafael J. Wysocki 
1111596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1112596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1113596ba34bSRafael J. Wysocki 		return -EINVAL;
1114596ba34bSRafael J. Wysocki 
1115d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
1116596ba34bSRafael J. Wysocki }
1117596ba34bSRafael J. Wysocki 
1118596ba34bSRafael J. Wysocki /**
11190496c8aeSRafael J. Wysocki  * pm_genpd_freeze - Freezing a device in an I/O PM domain.
1120596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1121596ba34bSRafael J. Wysocki  *
1122596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
1123596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1124596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1125596ba34bSRafael J. Wysocki  */
1126596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
1127596ba34bSRafael J. Wysocki {
1128596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1129596ba34bSRafael J. Wysocki 
1130596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1131596ba34bSRafael J. Wysocki 
1132596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1133596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1134596ba34bSRafael J. Wysocki 		return -EINVAL;
1135596ba34bSRafael J. Wysocki 
1136d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
1137596ba34bSRafael J. Wysocki }
1138596ba34bSRafael J. Wysocki 
1139596ba34bSRafael J. Wysocki /**
11400496c8aeSRafael J. Wysocki  * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
11410496c8aeSRafael J. Wysocki  * @dev: Device to freeze.
11420496c8aeSRafael J. Wysocki  *
11430496c8aeSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
11440496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
11450496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
11460496c8aeSRafael J. Wysocki  * devices.
11470496c8aeSRafael J. Wysocki  */
11480496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev)
11490496c8aeSRafael J. Wysocki {
11500496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
11510496c8aeSRafael J. Wysocki 
11520496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
11530496c8aeSRafael J. Wysocki 
11540496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
11550496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
11560496c8aeSRafael J. Wysocki 		return -EINVAL;
11570496c8aeSRafael J. Wysocki 
11580496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
11590496c8aeSRafael J. Wysocki }
11600496c8aeSRafael J. Wysocki 
11610496c8aeSRafael J. Wysocki /**
11620496c8aeSRafael J. Wysocki  * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1163596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1164596ba34bSRafael J. Wysocki  *
1165596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1166596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1167596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1168596ba34bSRafael J. Wysocki  * devices.
1169596ba34bSRafael J. Wysocki  */
1170596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
1171596ba34bSRafael J. Wysocki {
1172596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1173596ba34bSRafael J. Wysocki 
1174596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1175596ba34bSRafael J. Wysocki 
1176596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1177596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1178596ba34bSRafael J. Wysocki 		return -EINVAL;
1179596ba34bSRafael J. Wysocki 
1180dbf37414SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
1181596ba34bSRafael J. Wysocki }
1182596ba34bSRafael J. Wysocki 
1183596ba34bSRafael J. Wysocki /**
11840496c8aeSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1185596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1186596ba34bSRafael J. Wysocki  *
11870496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
11880496c8aeSRafael J. Wysocki  * before the system transition.
1189596ba34bSRafael J. Wysocki  */
1190596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
1191596ba34bSRafael J. Wysocki {
1192596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1193596ba34bSRafael J. Wysocki 
1194596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1195596ba34bSRafael J. Wysocki 
1196596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1197596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1198596ba34bSRafael J. Wysocki 		return -EINVAL;
1199596ba34bSRafael J. Wysocki 
1200dbf37414SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
12010496c8aeSRafael J. Wysocki }
1202596ba34bSRafael J. Wysocki 
12030496c8aeSRafael J. Wysocki /**
12040496c8aeSRafael J. Wysocki  * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
12050496c8aeSRafael J. Wysocki  * @dev: Device to thaw.
12060496c8aeSRafael J. Wysocki  *
12070496c8aeSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
12080496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
12090496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
12100496c8aeSRafael J. Wysocki  * devices.
12110496c8aeSRafael J. Wysocki  */
12120496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev)
12130496c8aeSRafael J. Wysocki {
12140496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1215596ba34bSRafael J. Wysocki 
12160496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
12170496c8aeSRafael J. Wysocki 
12180496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
12190496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
12200496c8aeSRafael J. Wysocki 		return -EINVAL;
12210496c8aeSRafael J. Wysocki 
12220496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
1223596ba34bSRafael J. Wysocki }
1224596ba34bSRafael J. Wysocki 
1225596ba34bSRafael J. Wysocki /**
1226596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1227596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1228596ba34bSRafael J. Wysocki  *
1229596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1230596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1231596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1232596ba34bSRafael J. Wysocki  */
1233596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1234596ba34bSRafael J. Wysocki {
1235596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1236596ba34bSRafael J. Wysocki 
1237596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1238596ba34bSRafael J. Wysocki 
1239596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1240596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1241596ba34bSRafael J. Wysocki 		return -EINVAL;
1242596ba34bSRafael J. Wysocki 
1243d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1244596ba34bSRafael J. Wysocki }
1245596ba34bSRafael J. Wysocki 
1246596ba34bSRafael J. Wysocki /**
12470496c8aeSRafael J. Wysocki  * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1248596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1249596ba34bSRafael J. Wysocki  *
12500496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
12510496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1252596ba34bSRafael J. Wysocki  */
1253596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1254596ba34bSRafael J. Wysocki {
1255596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1256596ba34bSRafael J. Wysocki 
1257596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1258596ba34bSRafael J. Wysocki 
1259596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1260596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1261596ba34bSRafael J. Wysocki 		return -EINVAL;
1262596ba34bSRafael J. Wysocki 
1263596ba34bSRafael J. Wysocki 	/*
1264596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1265596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1266596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
126765533bbfSRafael J. Wysocki 	 *
126865533bbfSRafael J. Wysocki 	 * At this point suspended_count == 0 means we are being run for the
126965533bbfSRafael J. Wysocki 	 * first time for the given domain in the present cycle.
127065533bbfSRafael J. Wysocki 	 */
127165533bbfSRafael J. Wysocki 	if (genpd->suspended_count++ == 0) {
127265533bbfSRafael J. Wysocki 		/*
127365533bbfSRafael J. Wysocki 		 * The boot kernel might put the domain into arbitrary state,
1274802d8b49SRafael J. Wysocki 		 * so make it appear as powered off to pm_genpd_sync_poweron(),
1275802d8b49SRafael J. Wysocki 		 * so that it tries to power it on in case it was really off.
1276596ba34bSRafael J. Wysocki 		 */
127717b75ecaSRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1278596ba34bSRafael J. Wysocki 		if (genpd->suspend_power_off) {
1279596ba34bSRafael J. Wysocki 			/*
128065533bbfSRafael J. Wysocki 			 * If the domain was off before the hibernation, make
128165533bbfSRafael J. Wysocki 			 * sure it will be off going forward.
1282596ba34bSRafael J. Wysocki 			 */
1283596ba34bSRafael J. Wysocki 			if (genpd->power_off)
1284596ba34bSRafael J. Wysocki 				genpd->power_off(genpd);
128565533bbfSRafael J. Wysocki 
1286596ba34bSRafael J. Wysocki 			return 0;
1287596ba34bSRafael J. Wysocki 		}
128865533bbfSRafael J. Wysocki 	}
1289596ba34bSRafael J. Wysocki 
129018dd2eceSRafael J. Wysocki 	if (genpd->suspend_power_off)
129118dd2eceSRafael J. Wysocki 		return 0;
129218dd2eceSRafael J. Wysocki 
1293802d8b49SRafael J. Wysocki 	pm_genpd_sync_poweron(genpd);
1294596ba34bSRafael J. Wysocki 
1295dbf37414SRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1296596ba34bSRafael J. Wysocki }
1297596ba34bSRafael J. Wysocki 
1298596ba34bSRafael J. Wysocki /**
1299596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1300596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1301596ba34bSRafael J. Wysocki  *
1302596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1303596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1304596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1305596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1306596ba34bSRafael J. Wysocki  */
1307596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1308596ba34bSRafael J. Wysocki {
1309596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1310596ba34bSRafael J. Wysocki 	bool run_complete;
1311596ba34bSRafael J. Wysocki 
1312596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1313596ba34bSRafael J. Wysocki 
1314596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1315596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1316596ba34bSRafael J. Wysocki 		return;
1317596ba34bSRafael J. Wysocki 
1318596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1319596ba34bSRafael J. Wysocki 
1320596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1321596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1322596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1323596ba34bSRafael J. Wysocki 
1324596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1325596ba34bSRafael J. Wysocki 
1326596ba34bSRafael J. Wysocki 	if (run_complete) {
1327596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
13286f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1329596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
1330af939339SUlf Hansson 		pm_request_idle(dev);
1331596ba34bSRafael J. Wysocki 	}
1332596ba34bSRafael J. Wysocki }
1333596ba34bSRafael J. Wysocki 
133477f827deSRafael J. Wysocki /**
133577f827deSRafael J. Wysocki  * pm_genpd_syscore_switch - Switch power during system core suspend or resume.
133677f827deSRafael J. Wysocki  * @dev: Device that normally is marked as "always on" to switch power for.
133777f827deSRafael J. Wysocki  *
133877f827deSRafael J. Wysocki  * This routine may only be called during the system core (syscore) suspend or
133977f827deSRafael J. Wysocki  * resume phase for devices whose "always on" flags are set.
134077f827deSRafael J. Wysocki  */
134177f827deSRafael J. Wysocki void pm_genpd_syscore_switch(struct device *dev, bool suspend)
134277f827deSRafael J. Wysocki {
134377f827deSRafael J. Wysocki 	struct generic_pm_domain *genpd;
134477f827deSRafael J. Wysocki 
134577f827deSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
134677f827deSRafael J. Wysocki 	if (!pm_genpd_present(genpd))
134777f827deSRafael J. Wysocki 		return;
134877f827deSRafael J. Wysocki 
134977f827deSRafael J. Wysocki 	if (suspend) {
135077f827deSRafael J. Wysocki 		genpd->suspended_count++;
135177f827deSRafael J. Wysocki 		pm_genpd_sync_poweroff(genpd);
135277f827deSRafael J. Wysocki 	} else {
135377f827deSRafael J. Wysocki 		pm_genpd_sync_poweron(genpd);
135477f827deSRafael J. Wysocki 		genpd->suspended_count--;
135577f827deSRafael J. Wysocki 	}
135677f827deSRafael J. Wysocki }
135777f827deSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch);
135877f827deSRafael J. Wysocki 
1359596ba34bSRafael J. Wysocki #else
1360596ba34bSRafael J. Wysocki 
1361596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1362596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
13630496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late		NULL
1364596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
13650496c8aeSRafael J. Wysocki #define pm_genpd_resume_early		NULL
1366596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1367596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1368596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
13690496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late		NULL
1370596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
13710496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early		NULL
1372596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1373596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1374596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1375596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1376596ba34bSRafael J. Wysocki 
1377596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1378596ba34bSRafael J. Wysocki 
13791d5fcfecSRafael J. Wysocki static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev)
13801d5fcfecSRafael J. Wysocki {
13811d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
13821d5fcfecSRafael J. Wysocki 
13831d5fcfecSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
13841d5fcfecSRafael J. Wysocki 	if (!gpd_data)
13851d5fcfecSRafael J. Wysocki 		return NULL;
13861d5fcfecSRafael J. Wysocki 
13871d5fcfecSRafael J. Wysocki 	mutex_init(&gpd_data->lock);
13881d5fcfecSRafael J. Wysocki 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
13891d5fcfecSRafael J. Wysocki 	dev_pm_qos_add_notifier(dev, &gpd_data->nb);
13901d5fcfecSRafael J. Wysocki 	return gpd_data;
13911d5fcfecSRafael J. Wysocki }
13921d5fcfecSRafael J. Wysocki 
13931d5fcfecSRafael J. Wysocki static void __pm_genpd_free_dev_data(struct device *dev,
13941d5fcfecSRafael J. Wysocki 				     struct generic_pm_domain_data *gpd_data)
13951d5fcfecSRafael J. Wysocki {
13961d5fcfecSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
13971d5fcfecSRafael J. Wysocki 	kfree(gpd_data);
13981d5fcfecSRafael J. Wysocki }
13991d5fcfecSRafael J. Wysocki 
1400f721889fSRafael J. Wysocki /**
1401b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1402f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1403f721889fSRafael J. Wysocki  * @dev: Device to be added.
1404b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1405f721889fSRafael J. Wysocki  */
1406b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1407b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1408f721889fSRafael J. Wysocki {
14091d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
14104605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1411f721889fSRafael J. Wysocki 	int ret = 0;
1412f721889fSRafael J. Wysocki 
1413f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1414f721889fSRafael J. Wysocki 
1415f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1416f721889fSRafael J. Wysocki 		return -EINVAL;
1417f721889fSRafael J. Wysocki 
14181d5fcfecSRafael J. Wysocki 	gpd_data_new = __pm_genpd_alloc_dev_data(dev);
14191d5fcfecSRafael J. Wysocki 	if (!gpd_data_new)
14206ff7bb0dSRafael J. Wysocki 		return -ENOMEM;
14216ff7bb0dSRafael J. Wysocki 
142217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1423f721889fSRafael J. Wysocki 
1424596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1425596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1426596ba34bSRafael J. Wysocki 		goto out;
1427596ba34bSRafael J. Wysocki 	}
1428596ba34bSRafael J. Wysocki 
14294605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
14304605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1431f721889fSRafael J. Wysocki 			ret = -EINVAL;
1432f721889fSRafael J. Wysocki 			goto out;
1433f721889fSRafael J. Wysocki 		}
1434f721889fSRafael J. Wysocki 
14351d5fcfecSRafael J. Wysocki 	ret = dev_pm_get_subsys_data(dev);
14361d5fcfecSRafael J. Wysocki 	if (ret)
14371d5fcfecSRafael J. Wysocki 		goto out;
14381d5fcfecSRafael J. Wysocki 
1439596ba34bSRafael J. Wysocki 	genpd->device_count++;
14406ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1441f721889fSRafael J. Wysocki 
14426ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
14431d5fcfecSRafael J. Wysocki 
14446ff7bb0dSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
14451d5fcfecSRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
14461d5fcfecSRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
14471d5fcfecSRafael J. Wysocki 	} else {
14481d5fcfecSRafael J. Wysocki 		gpd_data = gpd_data_new;
1449cd0ea672SRafael J. Wysocki 		dev->power.subsys_data->domain_data = &gpd_data->base;
14501d5fcfecSRafael J. Wysocki 	}
14511d5fcfecSRafael J. Wysocki 	gpd_data->refcount++;
1452b02c999aSRafael J. Wysocki 	if (td)
1453b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1454f721889fSRafael J. Wysocki 
14551d5fcfecSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
14561d5fcfecSRafael J. Wysocki 
14571d5fcfecSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
14581d5fcfecSRafael J. Wysocki 	gpd_data->base.dev = dev;
14591d5fcfecSRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
14601d5fcfecSRafael J. Wysocki 	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
14616ff7bb0dSRafael J. Wysocki 	gpd_data->td.constraint_changed = true;
14626ff7bb0dSRafael J. Wysocki 	gpd_data->td.effective_constraint_ns = -1;
14636ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
14646ff7bb0dSRafael J. Wysocki 
1465f721889fSRafael J. Wysocki  out:
146617b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1467f721889fSRafael J. Wysocki 
14681d5fcfecSRafael J. Wysocki 	if (gpd_data != gpd_data_new)
14691d5fcfecSRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data_new);
14701d5fcfecSRafael J. Wysocki 
1471f721889fSRafael J. Wysocki 	return ret;
1472f721889fSRafael J. Wysocki }
1473f721889fSRafael J. Wysocki 
1474f721889fSRafael J. Wysocki /**
1475c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1476c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1477c8aa130bSThomas Abraham  *   the device is added to.
1478c8aa130bSThomas Abraham  * @dev: Device to be added.
1479c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1480c8aa130bSThomas Abraham  */
1481c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1482c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1483c8aa130bSThomas Abraham {
1484c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1485c8aa130bSThomas Abraham 
1486c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1487c8aa130bSThomas Abraham 
1488c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1489c8aa130bSThomas Abraham 		return -EINVAL;
1490c8aa130bSThomas Abraham 
1491c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1492c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1493c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1494c8aa130bSThomas Abraham 			genpd = gpd;
1495c8aa130bSThomas Abraham 			break;
1496c8aa130bSThomas Abraham 		}
1497c8aa130bSThomas Abraham 	}
1498c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1499c8aa130bSThomas Abraham 
1500c8aa130bSThomas Abraham 	if (!genpd)
1501c8aa130bSThomas Abraham 		return -EINVAL;
1502c8aa130bSThomas Abraham 
1503c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1504c8aa130bSThomas Abraham }
1505c8aa130bSThomas Abraham 
1506b5abb085SRafael J. Wysocki 
1507b5abb085SRafael J. Wysocki /**
1508b5abb085SRafael J. Wysocki  * __pm_genpd_name_add_device - Find I/O PM domain and add a device to it.
1509b5abb085SRafael J. Wysocki  * @domain_name: Name of the PM domain to add the device to.
1510b5abb085SRafael J. Wysocki  * @dev: Device to be added.
1511b5abb085SRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1512b5abb085SRafael J. Wysocki  */
1513b5abb085SRafael J. Wysocki int __pm_genpd_name_add_device(const char *domain_name, struct device *dev,
1514b5abb085SRafael J. Wysocki 			       struct gpd_timing_data *td)
1515b5abb085SRafael J. Wysocki {
15168bc0251dSRafael J. Wysocki 	return __pm_genpd_add_device(pm_genpd_lookup_name(domain_name), dev, td);
1517b5abb085SRafael J. Wysocki }
1518b5abb085SRafael J. Wysocki 
1519c8aa130bSThomas Abraham /**
1520f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1521f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1522f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1523f721889fSRafael J. Wysocki  */
1524f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1525f721889fSRafael J. Wysocki 			   struct device *dev)
1526f721889fSRafael J. Wysocki {
15276ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
15284605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
15291d5fcfecSRafael J. Wysocki 	bool remove = false;
1530efa69025SRafael J. Wysocki 	int ret = 0;
1531f721889fSRafael J. Wysocki 
1532f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1533f721889fSRafael J. Wysocki 
1534efa69025SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
1535efa69025SRafael J. Wysocki 	    ||  IS_ERR_OR_NULL(dev->pm_domain)
1536efa69025SRafael J. Wysocki 	    ||  pd_to_genpd(dev->pm_domain) != genpd)
1537f721889fSRafael J. Wysocki 		return -EINVAL;
1538f721889fSRafael J. Wysocki 
153917b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1540f721889fSRafael J. Wysocki 
1541596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1542596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1543596ba34bSRafael J. Wysocki 		goto out;
1544596ba34bSRafael J. Wysocki 	}
1545596ba34bSRafael J. Wysocki 
15466ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
15476ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
15486ff7bb0dSRafael J. Wysocki 
15496ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
15501d5fcfecSRafael J. Wysocki 
1551f721889fSRafael J. Wysocki 	dev->pm_domain = NULL;
1552efa69025SRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1553efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
15541d5fcfecSRafael J. Wysocki 	gpd_data = to_gpd_data(pdd);
15551d5fcfecSRafael J. Wysocki 	if (--gpd_data->refcount == 0) {
1556efa69025SRafael J. Wysocki 		dev->power.subsys_data->domain_data = NULL;
15571d5fcfecSRafael J. Wysocki 		remove = true;
15581d5fcfecSRafael J. Wysocki 	}
15591d5fcfecSRafael J. Wysocki 
15606ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1561f721889fSRafael J. Wysocki 
15626ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
15636ff7bb0dSRafael J. Wysocki 	pdd->dev = NULL;
15646ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
15656ff7bb0dSRafael J. Wysocki 
15666ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
15676ff7bb0dSRafael J. Wysocki 
15686ff7bb0dSRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
15691d5fcfecSRafael J. Wysocki 	if (remove)
15701d5fcfecSRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data);
15711d5fcfecSRafael J. Wysocki 
15726ff7bb0dSRafael J. Wysocki 	return 0;
1573f721889fSRafael J. Wysocki 
1574596ba34bSRafael J. Wysocki  out:
157517b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1576f721889fSRafael J. Wysocki 
1577f721889fSRafael J. Wysocki 	return ret;
1578f721889fSRafael J. Wysocki }
1579f721889fSRafael J. Wysocki 
1580f721889fSRafael J. Wysocki /**
1581ca1d72f0SRafael J. Wysocki  * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
1582ca1d72f0SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
1583ca1d72f0SRafael J. Wysocki  * @val: The new value of the device's "need restore" flag.
1584ca1d72f0SRafael J. Wysocki  */
1585ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val)
1586ca1d72f0SRafael J. Wysocki {
1587ca1d72f0SRafael J. Wysocki 	struct pm_subsys_data *psd;
1588ca1d72f0SRafael J. Wysocki 	unsigned long flags;
1589ca1d72f0SRafael J. Wysocki 
1590ca1d72f0SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
1591ca1d72f0SRafael J. Wysocki 
1592ca1d72f0SRafael J. Wysocki 	psd = dev_to_psd(dev);
1593ca1d72f0SRafael J. Wysocki 	if (psd && psd->domain_data)
1594ca1d72f0SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->need_restore = val;
1595ca1d72f0SRafael J. Wysocki 
1596ca1d72f0SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1597ca1d72f0SRafael J. Wysocki }
1598ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
1599ca1d72f0SRafael J. Wysocki 
1600ca1d72f0SRafael J. Wysocki /**
1601f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1602f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1603bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1604f721889fSRafael J. Wysocki  */
1605f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1606bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1607f721889fSRafael J. Wysocki {
16085063ce15SRafael J. Wysocki 	struct gpd_link *link;
1609f721889fSRafael J. Wysocki 	int ret = 0;
1610f721889fSRafael J. Wysocki 
1611fb7268beSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)
1612fb7268beSRafael J. Wysocki 	    || genpd == subdomain)
1613f721889fSRafael J. Wysocki 		return -EINVAL;
1614f721889fSRafael J. Wysocki 
161517b75ecaSRafael J. Wysocki  start:
161617b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1617bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1618f721889fSRafael J. Wysocki 
1619bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1620bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1621bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
162217b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
162317b75ecaSRafael J. Wysocki 		goto start;
162417b75ecaSRafael J. Wysocki 	}
162517b75ecaSRafael J. Wysocki 
162617b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1627bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1628f721889fSRafael J. Wysocki 		ret = -EINVAL;
1629f721889fSRafael J. Wysocki 		goto out;
1630f721889fSRafael J. Wysocki 	}
1631f721889fSRafael J. Wysocki 
16324fcac10dSHuang Ying 	list_for_each_entry(link, &genpd->master_links, master_node) {
1633bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1634f721889fSRafael J. Wysocki 			ret = -EINVAL;
1635f721889fSRafael J. Wysocki 			goto out;
1636f721889fSRafael J. Wysocki 		}
1637f721889fSRafael J. Wysocki 	}
1638f721889fSRafael J. Wysocki 
16395063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
16405063ce15SRafael J. Wysocki 	if (!link) {
16415063ce15SRafael J. Wysocki 		ret = -ENOMEM;
16425063ce15SRafael J. Wysocki 		goto out;
16435063ce15SRafael J. Wysocki 	}
16445063ce15SRafael J. Wysocki 	link->master = genpd;
16455063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1646bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1647bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1648bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1649c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1650f721889fSRafael J. Wysocki 
1651f721889fSRafael J. Wysocki  out:
1652bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
165317b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1654f721889fSRafael J. Wysocki 
1655f721889fSRafael J. Wysocki 	return ret;
1656f721889fSRafael J. Wysocki }
1657f721889fSRafael J. Wysocki 
1658f721889fSRafael J. Wysocki /**
1659fb7268beSRafael J. Wysocki  * pm_genpd_add_subdomain_names - Add a subdomain to an I/O PM domain.
1660fb7268beSRafael J. Wysocki  * @master_name: Name of the master PM domain to add the subdomain to.
1661fb7268beSRafael J. Wysocki  * @subdomain_name: Name of the subdomain to be added.
1662fb7268beSRafael J. Wysocki  */
1663fb7268beSRafael J. Wysocki int pm_genpd_add_subdomain_names(const char *master_name,
1664fb7268beSRafael J. Wysocki 				 const char *subdomain_name)
1665fb7268beSRafael J. Wysocki {
1666fb7268beSRafael J. Wysocki 	struct generic_pm_domain *master = NULL, *subdomain = NULL, *gpd;
1667fb7268beSRafael J. Wysocki 
1668fb7268beSRafael J. Wysocki 	if (IS_ERR_OR_NULL(master_name) || IS_ERR_OR_NULL(subdomain_name))
1669fb7268beSRafael J. Wysocki 		return -EINVAL;
1670fb7268beSRafael J. Wysocki 
1671fb7268beSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
1672fb7268beSRafael J. Wysocki 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1673fb7268beSRafael J. Wysocki 		if (!master && !strcmp(gpd->name, master_name))
1674fb7268beSRafael J. Wysocki 			master = gpd;
1675fb7268beSRafael J. Wysocki 
1676fb7268beSRafael J. Wysocki 		if (!subdomain && !strcmp(gpd->name, subdomain_name))
1677fb7268beSRafael J. Wysocki 			subdomain = gpd;
1678fb7268beSRafael J. Wysocki 
1679fb7268beSRafael J. Wysocki 		if (master && subdomain)
1680fb7268beSRafael J. Wysocki 			break;
1681fb7268beSRafael J. Wysocki 	}
1682fb7268beSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
1683fb7268beSRafael J. Wysocki 
1684fb7268beSRafael J. Wysocki 	return pm_genpd_add_subdomain(master, subdomain);
1685fb7268beSRafael J. Wysocki }
1686fb7268beSRafael J. Wysocki 
1687fb7268beSRafael J. Wysocki /**
1688f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1689f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
16905063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1691f721889fSRafael J. Wysocki  */
1692f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
16935063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1694f721889fSRafael J. Wysocki {
16955063ce15SRafael J. Wysocki 	struct gpd_link *link;
1696f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1697f721889fSRafael J. Wysocki 
16985063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1699f721889fSRafael J. Wysocki 		return -EINVAL;
1700f721889fSRafael J. Wysocki 
170117b75ecaSRafael J. Wysocki  start:
170217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1703f721889fSRafael J. Wysocki 
17045063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
17055063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1706f721889fSRafael J. Wysocki 			continue;
1707f721889fSRafael J. Wysocki 
1708f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1709f721889fSRafael J. Wysocki 
171017b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
171117b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
171217b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
171317b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
171417b75ecaSRafael J. Wysocki 			goto start;
171517b75ecaSRafael J. Wysocki 		}
171617b75ecaSRafael J. Wysocki 
17175063ce15SRafael J. Wysocki 		list_del(&link->master_node);
17185063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
17195063ce15SRafael J. Wysocki 		kfree(link);
172017b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1721f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1722f721889fSRafael J. Wysocki 
1723f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1724f721889fSRafael J. Wysocki 
1725f721889fSRafael J. Wysocki 		ret = 0;
1726f721889fSRafael J. Wysocki 		break;
1727f721889fSRafael J. Wysocki 	}
1728f721889fSRafael J. Wysocki 
172917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1730f721889fSRafael J. Wysocki 
1731f721889fSRafael J. Wysocki 	return ret;
1732f721889fSRafael J. Wysocki }
1733f721889fSRafael J. Wysocki 
1734f721889fSRafael J. Wysocki /**
1735d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1736d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1737d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1738b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
173962d44902SRafael J. Wysocki  *
174062d44902SRafael J. Wysocki  * Every call to this routine should be balanced with a call to
174162d44902SRafael J. Wysocki  * __pm_genpd_remove_callbacks() and they must not be nested.
1742d5e4cbfeSRafael J. Wysocki  */
1743b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1744b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1745d5e4cbfeSRafael J. Wysocki {
174662d44902SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
1747d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1748d5e4cbfeSRafael J. Wysocki 
174962d44902SRafael J. Wysocki 	if (!(dev && ops))
1750d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1751d5e4cbfeSRafael J. Wysocki 
175262d44902SRafael J. Wysocki 	gpd_data_new = __pm_genpd_alloc_dev_data(dev);
175362d44902SRafael J. Wysocki 	if (!gpd_data_new)
175462d44902SRafael J. Wysocki 		return -ENOMEM;
175562d44902SRafael J. Wysocki 
1756d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1757d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1758d5e4cbfeSRafael J. Wysocki 
175962d44902SRafael J. Wysocki 	ret = dev_pm_get_subsys_data(dev);
176062d44902SRafael J. Wysocki 	if (ret)
176162d44902SRafael J. Wysocki 		goto out;
1762d5e4cbfeSRafael J. Wysocki 
176362d44902SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
176462d44902SRafael J. Wysocki 
176562d44902SRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
176662d44902SRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
176762d44902SRafael J. Wysocki 	} else {
176862d44902SRafael J. Wysocki 		gpd_data = gpd_data_new;
176962d44902SRafael J. Wysocki 		dev->power.subsys_data->domain_data = &gpd_data->base;
177062d44902SRafael J. Wysocki 	}
177162d44902SRafael J. Wysocki 	gpd_data->refcount++;
1772d5e4cbfeSRafael J. Wysocki 	gpd_data->ops = *ops;
1773b02c999aSRafael J. Wysocki 	if (td)
1774b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1775d5e4cbfeSRafael J. Wysocki 
177662d44902SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
177762d44902SRafael J. Wysocki 
177862d44902SRafael J. Wysocki  out:
1779d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1780d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1781d5e4cbfeSRafael J. Wysocki 
178262d44902SRafael J. Wysocki 	if (gpd_data != gpd_data_new)
178362d44902SRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data_new);
178462d44902SRafael J. Wysocki 
1785d5e4cbfeSRafael J. Wysocki 	return ret;
1786d5e4cbfeSRafael J. Wysocki }
1787d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1788d5e4cbfeSRafael J. Wysocki 
1789d5e4cbfeSRafael J. Wysocki /**
1790b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1791d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1792b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
179362d44902SRafael J. Wysocki  *
179462d44902SRafael J. Wysocki  * This routine can only be called after pm_genpd_add_callbacks().
1795d5e4cbfeSRafael J. Wysocki  */
1796b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1797d5e4cbfeSRafael J. Wysocki {
179862d44902SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = NULL;
179962d44902SRafael J. Wysocki 	bool remove = false;
1800d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1801d5e4cbfeSRafael J. Wysocki 
1802d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1803d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1804d5e4cbfeSRafael J. Wysocki 
1805d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1806d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1807d5e4cbfeSRafael J. Wysocki 
180862d44902SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1809d5e4cbfeSRafael J. Wysocki 
181062d44902SRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
181162d44902SRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1812db79e53dSSachin Kamat 		gpd_data->ops = (struct gpd_dev_ops){ NULL };
1813b02c999aSRafael J. Wysocki 		if (clear_td)
1814b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
181562d44902SRafael J. Wysocki 
181662d44902SRafael J. Wysocki 		if (--gpd_data->refcount == 0) {
181762d44902SRafael J. Wysocki 			dev->power.subsys_data->domain_data = NULL;
181862d44902SRafael J. Wysocki 			remove = true;
181962d44902SRafael J. Wysocki 		}
1820d5e4cbfeSRafael J. Wysocki 	} else {
1821d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1822d5e4cbfeSRafael J. Wysocki 	}
1823d5e4cbfeSRafael J. Wysocki 
182462d44902SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
182562d44902SRafael J. Wysocki 
1826d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1827d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1828d5e4cbfeSRafael J. Wysocki 
182962d44902SRafael J. Wysocki 	if (ret)
1830d5e4cbfeSRafael J. Wysocki 		return ret;
183162d44902SRafael J. Wysocki 
183262d44902SRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
183362d44902SRafael J. Wysocki 	if (remove)
183462d44902SRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data);
183562d44902SRafael J. Wysocki 
183662d44902SRafael J. Wysocki 	return 0;
1837d5e4cbfeSRafael J. Wysocki }
1838b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1839d5e4cbfeSRafael J. Wysocki 
184040114447SRafael J. Wysocki /**
184140114447SRafael J. Wysocki  * pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle.
184240114447SRafael J. Wysocki  * @genpd: PM domain to be connected with cpuidle.
184340114447SRafael J. Wysocki  * @state: cpuidle state this domain can disable/enable.
184440114447SRafael J. Wysocki  *
184540114447SRafael J. Wysocki  * Make a PM domain behave as though it contained a CPU core, that is, instead
184640114447SRafael J. Wysocki  * of calling its power down routine it will enable the given cpuidle state so
184740114447SRafael J. Wysocki  * that the cpuidle subsystem can power it down (if possible and desirable).
184840114447SRafael J. Wysocki  */
184940114447SRafael J. Wysocki int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
1850cbc9ef02SRafael J. Wysocki {
1851cbc9ef02SRafael J. Wysocki 	struct cpuidle_driver *cpuidle_drv;
1852cbc9ef02SRafael J. Wysocki 	struct gpd_cpu_data *cpu_data;
1853cbc9ef02SRafael J. Wysocki 	struct cpuidle_state *idle_state;
1854cbc9ef02SRafael J. Wysocki 	int ret = 0;
1855cbc9ef02SRafael J. Wysocki 
1856cbc9ef02SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || state < 0)
1857cbc9ef02SRafael J. Wysocki 		return -EINVAL;
1858cbc9ef02SRafael J. Wysocki 
1859cbc9ef02SRafael J. Wysocki 	genpd_acquire_lock(genpd);
1860cbc9ef02SRafael J. Wysocki 
1861cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
1862cbc9ef02SRafael J. Wysocki 		ret = -EEXIST;
1863cbc9ef02SRafael J. Wysocki 		goto out;
1864cbc9ef02SRafael J. Wysocki 	}
1865cbc9ef02SRafael J. Wysocki 	cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL);
1866cbc9ef02SRafael J. Wysocki 	if (!cpu_data) {
1867cbc9ef02SRafael J. Wysocki 		ret = -ENOMEM;
1868cbc9ef02SRafael J. Wysocki 		goto out;
1869cbc9ef02SRafael J. Wysocki 	}
1870cbc9ef02SRafael J. Wysocki 	cpuidle_drv = cpuidle_driver_ref();
1871cbc9ef02SRafael J. Wysocki 	if (!cpuidle_drv) {
1872cbc9ef02SRafael J. Wysocki 		ret = -ENODEV;
1873debe081aSjhbird.choi@samsung.com 		goto err_drv;
1874cbc9ef02SRafael J. Wysocki 	}
1875cbc9ef02SRafael J. Wysocki 	if (cpuidle_drv->state_count <= state) {
1876cbc9ef02SRafael J. Wysocki 		ret = -EINVAL;
1877cbc9ef02SRafael J. Wysocki 		goto err;
1878cbc9ef02SRafael J. Wysocki 	}
1879cbc9ef02SRafael J. Wysocki 	idle_state = &cpuidle_drv->states[state];
1880cbc9ef02SRafael J. Wysocki 	if (!idle_state->disabled) {
1881cbc9ef02SRafael J. Wysocki 		ret = -EAGAIN;
1882cbc9ef02SRafael J. Wysocki 		goto err;
1883cbc9ef02SRafael J. Wysocki 	}
1884cbc9ef02SRafael J. Wysocki 	cpu_data->idle_state = idle_state;
1885cbc9ef02SRafael J. Wysocki 	cpu_data->saved_exit_latency = idle_state->exit_latency;
1886cbc9ef02SRafael J. Wysocki 	genpd->cpu_data = cpu_data;
1887cbc9ef02SRafael J. Wysocki 	genpd_recalc_cpu_exit_latency(genpd);
1888cbc9ef02SRafael J. Wysocki 
1889cbc9ef02SRafael J. Wysocki  out:
1890cbc9ef02SRafael J. Wysocki 	genpd_release_lock(genpd);
1891cbc9ef02SRafael J. Wysocki 	return ret;
1892cbc9ef02SRafael J. Wysocki 
1893cbc9ef02SRafael J. Wysocki  err:
1894cbc9ef02SRafael J. Wysocki 	cpuidle_driver_unref();
1895debe081aSjhbird.choi@samsung.com 
1896debe081aSjhbird.choi@samsung.com  err_drv:
1897debe081aSjhbird.choi@samsung.com 	kfree(cpu_data);
1898cbc9ef02SRafael J. Wysocki 	goto out;
1899cbc9ef02SRafael J. Wysocki }
1900cbc9ef02SRafael J. Wysocki 
190140114447SRafael J. Wysocki /**
190274a2799aSRafael J. Wysocki  * pm_genpd_name_attach_cpuidle - Find PM domain and connect cpuidle to it.
190374a2799aSRafael J. Wysocki  * @name: Name of the domain to connect to cpuidle.
190474a2799aSRafael J. Wysocki  * @state: cpuidle state this domain can manipulate.
190574a2799aSRafael J. Wysocki  */
190674a2799aSRafael J. Wysocki int pm_genpd_name_attach_cpuidle(const char *name, int state)
190774a2799aSRafael J. Wysocki {
190874a2799aSRafael J. Wysocki 	return pm_genpd_attach_cpuidle(pm_genpd_lookup_name(name), state);
190974a2799aSRafael J. Wysocki }
191074a2799aSRafael J. Wysocki 
191174a2799aSRafael J. Wysocki /**
191240114447SRafael J. Wysocki  * pm_genpd_detach_cpuidle - Remove the cpuidle connection from a PM domain.
191340114447SRafael J. Wysocki  * @genpd: PM domain to remove the cpuidle connection from.
191440114447SRafael J. Wysocki  *
191540114447SRafael J. Wysocki  * Remove the cpuidle connection set up by pm_genpd_attach_cpuidle() from the
191640114447SRafael J. Wysocki  * given PM domain.
191740114447SRafael J. Wysocki  */
191840114447SRafael J. Wysocki int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd)
1919cbc9ef02SRafael J. Wysocki {
1920cbc9ef02SRafael J. Wysocki 	struct gpd_cpu_data *cpu_data;
1921cbc9ef02SRafael J. Wysocki 	struct cpuidle_state *idle_state;
1922cbc9ef02SRafael J. Wysocki 	int ret = 0;
1923cbc9ef02SRafael J. Wysocki 
1924cbc9ef02SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1925cbc9ef02SRafael J. Wysocki 		return -EINVAL;
1926cbc9ef02SRafael J. Wysocki 
1927cbc9ef02SRafael J. Wysocki 	genpd_acquire_lock(genpd);
1928cbc9ef02SRafael J. Wysocki 
1929cbc9ef02SRafael J. Wysocki 	cpu_data = genpd->cpu_data;
1930cbc9ef02SRafael J. Wysocki 	if (!cpu_data) {
1931cbc9ef02SRafael J. Wysocki 		ret = -ENODEV;
1932cbc9ef02SRafael J. Wysocki 		goto out;
1933cbc9ef02SRafael J. Wysocki 	}
1934cbc9ef02SRafael J. Wysocki 	idle_state = cpu_data->idle_state;
1935cbc9ef02SRafael J. Wysocki 	if (!idle_state->disabled) {
1936cbc9ef02SRafael J. Wysocki 		ret = -EAGAIN;
1937cbc9ef02SRafael J. Wysocki 		goto out;
1938cbc9ef02SRafael J. Wysocki 	}
1939cbc9ef02SRafael J. Wysocki 	idle_state->exit_latency = cpu_data->saved_exit_latency;
1940cbc9ef02SRafael J. Wysocki 	cpuidle_driver_unref();
1941cbc9ef02SRafael J. Wysocki 	genpd->cpu_data = NULL;
1942cbc9ef02SRafael J. Wysocki 	kfree(cpu_data);
1943cbc9ef02SRafael J. Wysocki 
1944cbc9ef02SRafael J. Wysocki  out:
1945cbc9ef02SRafael J. Wysocki 	genpd_release_lock(genpd);
1946cbc9ef02SRafael J. Wysocki 	return ret;
1947cbc9ef02SRafael J. Wysocki }
1948cbc9ef02SRafael J. Wysocki 
194974a2799aSRafael J. Wysocki /**
195074a2799aSRafael J. Wysocki  * pm_genpd_name_detach_cpuidle - Find PM domain and disconnect cpuidle from it.
195174a2799aSRafael J. Wysocki  * @name: Name of the domain to disconnect cpuidle from.
195274a2799aSRafael J. Wysocki  */
195374a2799aSRafael J. Wysocki int pm_genpd_name_detach_cpuidle(const char *name)
195474a2799aSRafael J. Wysocki {
195574a2799aSRafael J. Wysocki 	return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name));
195674a2799aSRafael J. Wysocki }
195774a2799aSRafael J. Wysocki 
1958d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1959d23b9b00SRafael J. Wysocki 
1960d5e4cbfeSRafael J. Wysocki /**
1961ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1962ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1963ecf00475SRafael J. Wysocki  */
1964ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1965ecf00475SRafael J. Wysocki {
1966ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1967ecf00475SRafael J. Wysocki 
1968ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1969ecf00475SRafael J. Wysocki 	if (cb)
1970ecf00475SRafael J. Wysocki 		return cb(dev);
1971ecf00475SRafael J. Wysocki 
19720b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
19730b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_suspend;
19740b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
19750b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_suspend;
19760b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
19770b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_suspend;
19780b589741SRafael J. Wysocki 	else
19790b589741SRafael J. Wysocki 		cb = NULL;
1980ecf00475SRafael J. Wysocki 
19810b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
19820b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_suspend;
19830b589741SRafael J. Wysocki 
19840b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1985ecf00475SRafael J. Wysocki }
1986ecf00475SRafael J. Wysocki 
1987ecf00475SRafael J. Wysocki /**
1988ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1989ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1990ecf00475SRafael J. Wysocki  */
1991ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1992ecf00475SRafael J. Wysocki {
1993ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1994ecf00475SRafael J. Wysocki 
1995ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1996ecf00475SRafael J. Wysocki 	if (cb)
1997ecf00475SRafael J. Wysocki 		return cb(dev);
1998ecf00475SRafael J. Wysocki 
19990b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
20000b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_resume;
20010b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
20020b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_resume;
20030b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
20040b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_resume;
20050b589741SRafael J. Wysocki 	else
20060b589741SRafael J. Wysocki 		cb = NULL;
2007ecf00475SRafael J. Wysocki 
20080b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
20090b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_resume;
20100b589741SRafael J. Wysocki 
20110b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
2012ecf00475SRafael J. Wysocki }
2013ecf00475SRafael J. Wysocki 
20140f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
20150f1d6986SRafael J. Wysocki 
2016ecf00475SRafael J. Wysocki /**
2017d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
2018d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2019d23b9b00SRafael J. Wysocki  */
2020d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
2021d23b9b00SRafael J. Wysocki {
2022c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
2023d23b9b00SRafael J. Wysocki 
2024d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
2025d23b9b00SRafael J. Wysocki }
2026d23b9b00SRafael J. Wysocki 
2027d23b9b00SRafael J. Wysocki /**
2028d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
2029d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2030d23b9b00SRafael J. Wysocki  */
2031d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
2032d23b9b00SRafael J. Wysocki {
2033c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
2034d23b9b00SRafael J. Wysocki 
20350496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_late(dev);
2036d23b9b00SRafael J. Wysocki }
2037d23b9b00SRafael J. Wysocki 
2038d23b9b00SRafael J. Wysocki /**
2039d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
2040d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2041d23b9b00SRafael J. Wysocki  */
2042d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
2043d23b9b00SRafael J. Wysocki {
2044c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
2045d23b9b00SRafael J. Wysocki 
20460496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_early(dev);
2047d23b9b00SRafael J. Wysocki }
2048d23b9b00SRafael J. Wysocki 
2049d23b9b00SRafael J. Wysocki /**
2050d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
2051d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2052d23b9b00SRafael J. Wysocki  */
2053d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
2054d23b9b00SRafael J. Wysocki {
2055c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
2056d23b9b00SRafael J. Wysocki 
2057d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
2058d23b9b00SRafael J. Wysocki }
2059d23b9b00SRafael J. Wysocki 
2060d23b9b00SRafael J. Wysocki /**
2061d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
2062d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2063d23b9b00SRafael J. Wysocki  */
2064d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
2065d23b9b00SRafael J. Wysocki {
2066d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
2067d23b9b00SRafael J. Wysocki 
2068d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
2069d23b9b00SRafael J. Wysocki }
2070d23b9b00SRafael J. Wysocki 
2071d23b9b00SRafael J. Wysocki /**
2072d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
2073d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2074d23b9b00SRafael J. Wysocki  */
2075d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
2076d23b9b00SRafael J. Wysocki {
2077d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
2078d23b9b00SRafael J. Wysocki 
20790496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_late(dev);
2080d23b9b00SRafael J. Wysocki }
2081d23b9b00SRafael J. Wysocki 
2082d23b9b00SRafael J. Wysocki /**
2083d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
2084d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2085d23b9b00SRafael J. Wysocki  */
2086d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
2087d23b9b00SRafael J. Wysocki {
2088d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
2089d23b9b00SRafael J. Wysocki 
20900496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_early(dev);
2091d23b9b00SRafael J. Wysocki }
2092d23b9b00SRafael J. Wysocki 
2093d23b9b00SRafael J. Wysocki /**
2094d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
2095d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
2096d23b9b00SRafael J. Wysocki  */
2097d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
2098d23b9b00SRafael J. Wysocki {
2099d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
2100d23b9b00SRafael J. Wysocki 
2101d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
2102d23b9b00SRafael J. Wysocki }
2103d23b9b00SRafael J. Wysocki 
21040f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
21050f1d6986SRafael J. Wysocki 
21060f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
21070f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
21080f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
21090f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
21100f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
21110f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
21120f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
21130f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
21140f1d6986SRafael J. Wysocki 
21150f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
21160f1d6986SRafael J. Wysocki 
2117d23b9b00SRafael J. Wysocki /**
2118f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
2119f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
2120f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
2121f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
2122f721889fSRafael J. Wysocki  */
2123f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
2124f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
2125f721889fSRafael J. Wysocki {
2126f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
2127f721889fSRafael J. Wysocki 		return;
2128f721889fSRafael J. Wysocki 
21295063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
21305063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
2131f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
2132f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
2133f721889fSRafael J. Wysocki 	genpd->gov = gov;
2134f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
2135f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
2136c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
213717b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
213817b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
2139c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
2140c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
2141596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
2142221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
21436ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
2144f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
2145f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
2146f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
2147596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
2148596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
21490496c8aeSRafael J. Wysocki 	genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
2150596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
2151596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
21520496c8aeSRafael J. Wysocki 	genpd->domain.ops.resume_early = pm_genpd_resume_early;
2153596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
2154596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
21550496c8aeSRafael J. Wysocki 	genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
2156596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
2157596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
21580496c8aeSRafael J. Wysocki 	genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
2159596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
2160d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
21610496c8aeSRafael J. Wysocki 	genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
2162d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
2163596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
21640496c8aeSRafael J. Wysocki 	genpd->domain.ops.restore_early = pm_genpd_resume_early;
2165d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
2166596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
2167ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
2168ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
2169c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
2170c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
2171c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
2172c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
2173d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
2174d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
2175d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
2176d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
21775125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
21785125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
21795125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
21805125bbf3SRafael J. Wysocki }
2181