xref: /openbmc/linux/drivers/base/power/domain.c (revision e2e3e4e5)
1f721889fSRafael J. Wysocki /*
2f721889fSRafael J. Wysocki  * drivers/base/power/domain.c - Common code related to device power domains.
3f721889fSRafael J. Wysocki  *
4f721889fSRafael J. Wysocki  * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
5f721889fSRafael J. Wysocki  *
6f721889fSRafael J. Wysocki  * This file is released under the GPLv2.
7f721889fSRafael J. Wysocki  */
8f721889fSRafael J. Wysocki 
9f721889fSRafael J. Wysocki #include <linux/init.h>
10f721889fSRafael J. Wysocki #include <linux/kernel.h>
11f721889fSRafael J. Wysocki #include <linux/io.h>
12f721889fSRafael J. Wysocki #include <linux/pm_runtime.h>
13f721889fSRafael J. Wysocki #include <linux/pm_domain.h>
146ff7bb0dSRafael J. Wysocki #include <linux/pm_qos.h>
15f721889fSRafael J. Wysocki #include <linux/slab.h>
16f721889fSRafael J. Wysocki #include <linux/err.h>
1717b75ecaSRafael J. Wysocki #include <linux/sched.h>
1817b75ecaSRafael J. Wysocki #include <linux/suspend.h>
19d5e4cbfeSRafael J. Wysocki #include <linux/export.h>
20d5e4cbfeSRafael J. Wysocki 
21d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev)		\
22d5e4cbfeSRafael J. Wysocki ({								\
23d5e4cbfeSRafael J. Wysocki 	type (*__routine)(struct device *__d); 			\
24d5e4cbfeSRafael J. Wysocki 	type __ret = (type)0;					\
25d5e4cbfeSRafael J. Wysocki 								\
26d5e4cbfeSRafael J. Wysocki 	__routine = genpd->dev_ops.callback; 			\
27d5e4cbfeSRafael J. Wysocki 	if (__routine) {					\
28d5e4cbfeSRafael J. Wysocki 		__ret = __routine(dev); 			\
29d5e4cbfeSRafael J. Wysocki 	} else {						\
30d5e4cbfeSRafael J. Wysocki 		__routine = dev_gpd_data(dev)->ops.callback;	\
31d5e4cbfeSRafael J. Wysocki 		if (__routine) 					\
32d5e4cbfeSRafael J. Wysocki 			__ret = __routine(dev);			\
33d5e4cbfeSRafael J. Wysocki 	}							\
34d5e4cbfeSRafael J. Wysocki 	__ret;							\
35d5e4cbfeSRafael J. Wysocki })
36f721889fSRafael J. Wysocki 
370140d8bdSRafael J. Wysocki #define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name)	\
380140d8bdSRafael J. Wysocki ({										\
390140d8bdSRafael J. Wysocki 	ktime_t __start = ktime_get();						\
400140d8bdSRafael J. Wysocki 	type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);		\
410140d8bdSRafael J. Wysocki 	s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));		\
426ff7bb0dSRafael J. Wysocki 	struct gpd_timing_data *__td = &dev_gpd_data(dev)->td;			\
436ff7bb0dSRafael J. Wysocki 	if (!__retval && __elapsed > __td->field) {				\
446ff7bb0dSRafael J. Wysocki 		__td->field = __elapsed;					\
450140d8bdSRafael J. Wysocki 		dev_warn(dev, name " latency exceeded, new value %lld ns\n",	\
460140d8bdSRafael J. Wysocki 			__elapsed);						\
476ff7bb0dSRafael J. Wysocki 		genpd->max_off_time_changed = true;				\
486ff7bb0dSRafael J. Wysocki 		__td->constraint_changed = true;				\
490140d8bdSRafael J. Wysocki 	}									\
500140d8bdSRafael J. Wysocki 	__retval;								\
510140d8bdSRafael J. Wysocki })
520140d8bdSRafael J. Wysocki 
535125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list);
545125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock);
555125bbf3SRafael J. Wysocki 
565248051bSRafael J. Wysocki #ifdef CONFIG_PM
575248051bSRafael J. Wysocki 
58b02c999aSRafael J. Wysocki struct generic_pm_domain *dev_to_genpd(struct device *dev)
595248051bSRafael J. Wysocki {
605248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
615248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
625248051bSRafael J. Wysocki 
63596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
645248051bSRafael J. Wysocki }
65f721889fSRafael J. Wysocki 
66d5e4cbfeSRafael J. Wysocki static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
67d5e4cbfeSRafael J. Wysocki {
680140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev,
690140d8bdSRafael J. Wysocki 					stop_latency_ns, "stop");
70d5e4cbfeSRafael J. Wysocki }
71d5e4cbfeSRafael J. Wysocki 
72d5e4cbfeSRafael J. Wysocki static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
73d5e4cbfeSRafael J. Wysocki {
740140d8bdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev,
750140d8bdSRafael J. Wysocki 					start_latency_ns, "start");
76d5e4cbfeSRafael J. Wysocki }
77d5e4cbfeSRafael J. Wysocki 
78e2e3e4e5SRafael J. Wysocki static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd,
79e2e3e4e5SRafael J. Wysocki 				     struct device *dev)
80e2e3e4e5SRafael J. Wysocki {
81e2e3e4e5SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, start, dev);
82e2e3e4e5SRafael J. Wysocki }
83e2e3e4e5SRafael J. Wysocki 
84c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
85f721889fSRafael J. Wysocki {
86c4bb3160SRafael J. Wysocki 	bool ret = false;
87c4bb3160SRafael J. Wysocki 
88c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
89c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
90c4bb3160SRafael J. Wysocki 
91c4bb3160SRafael J. Wysocki 	return ret;
92c4bb3160SRafael J. Wysocki }
93c4bb3160SRafael J. Wysocki 
94c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
95c4bb3160SRafael J. Wysocki {
96c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
97c4bb3160SRafael J. Wysocki 	smp_mb__after_atomic_inc();
98f721889fSRafael J. Wysocki }
99f721889fSRafael J. Wysocki 
10017b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd)
10117b75ecaSRafael J. Wysocki {
10217b75ecaSRafael J. Wysocki 	DEFINE_WAIT(wait);
10317b75ecaSRafael J. Wysocki 
10417b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
10517b75ecaSRafael J. Wysocki 	/*
10617b75ecaSRafael J. Wysocki 	 * Wait for the domain to transition into either the active,
10717b75ecaSRafael J. Wysocki 	 * or the power off state.
10817b75ecaSRafael J. Wysocki 	 */
10917b75ecaSRafael J. Wysocki 	for (;;) {
11017b75ecaSRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
11117b75ecaSRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
112c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_ACTIVE
113c6d22b37SRafael J. Wysocki 		    || genpd->status == GPD_STATE_POWER_OFF)
11417b75ecaSRafael J. Wysocki 			break;
11517b75ecaSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
11617b75ecaSRafael J. Wysocki 
11717b75ecaSRafael J. Wysocki 		schedule();
11817b75ecaSRafael J. Wysocki 
11917b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
12017b75ecaSRafael J. Wysocki 	}
12117b75ecaSRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
12217b75ecaSRafael J. Wysocki }
12317b75ecaSRafael J. Wysocki 
12417b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd)
12517b75ecaSRafael J. Wysocki {
12617b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
12717b75ecaSRafael J. Wysocki }
12817b75ecaSRafael J. Wysocki 
129c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd)
130c6d22b37SRafael J. Wysocki {
131c6d22b37SRafael J. Wysocki 	if (genpd->resume_count == 0)
132c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_ACTIVE;
133c6d22b37SRafael J. Wysocki }
134c6d22b37SRafael J. Wysocki 
135cbc9ef02SRafael J. Wysocki static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
136cbc9ef02SRafael J. Wysocki {
137cbc9ef02SRafael J. Wysocki 	s64 usecs64;
138cbc9ef02SRafael J. Wysocki 
139cbc9ef02SRafael J. Wysocki 	if (!genpd->cpu_data)
140cbc9ef02SRafael J. Wysocki 		return;
141cbc9ef02SRafael J. Wysocki 
142cbc9ef02SRafael J. Wysocki 	usecs64 = genpd->power_on_latency_ns;
143cbc9ef02SRafael J. Wysocki 	do_div(usecs64, NSEC_PER_USEC);
144cbc9ef02SRafael J. Wysocki 	usecs64 += genpd->cpu_data->saved_exit_latency;
145cbc9ef02SRafael J. Wysocki 	genpd->cpu_data->idle_state->exit_latency = usecs64;
146cbc9ef02SRafael J. Wysocki }
147cbc9ef02SRafael J. Wysocki 
148f721889fSRafael J. Wysocki /**
1495063ce15SRafael J. Wysocki  * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
1505248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
1515248051bSRafael J. Wysocki  *
1525063ce15SRafael J. Wysocki  * Restore power to @genpd and all of its masters so that it is possible to
1535248051bSRafael J. Wysocki  * resume a device belonging to it.
1545248051bSRafael J. Wysocki  */
1558951ef02SSachin Kamat static int __pm_genpd_poweron(struct generic_pm_domain *genpd)
1563f241775SRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
1575248051bSRafael J. Wysocki {
1585063ce15SRafael J. Wysocki 	struct gpd_link *link;
1593f241775SRafael J. Wysocki 	DEFINE_WAIT(wait);
1605248051bSRafael J. Wysocki 	int ret = 0;
1615248051bSRafael J. Wysocki 
1625063ce15SRafael J. Wysocki 	/* If the domain's master is being waited for, we have to wait too. */
1633f241775SRafael J. Wysocki 	for (;;) {
1643f241775SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
1653f241775SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
16617877eb5SRafael J. Wysocki 		if (genpd->status != GPD_STATE_WAIT_MASTER)
1673f241775SRafael J. Wysocki 			break;
1683f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
1693f241775SRafael J. Wysocki 
1703f241775SRafael J. Wysocki 		schedule();
1713f241775SRafael J. Wysocki 
17217b75ecaSRafael J. Wysocki 		mutex_lock(&genpd->lock);
1733f241775SRafael J. Wysocki 	}
1743f241775SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
17517b75ecaSRafael J. Wysocki 
17617b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_ACTIVE
177596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
1783f241775SRafael J. Wysocki 		return 0;
1795248051bSRafael J. Wysocki 
180c6d22b37SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF) {
181c6d22b37SRafael J. Wysocki 		genpd_set_active(genpd);
1823f241775SRafael J. Wysocki 		return 0;
183c6d22b37SRafael J. Wysocki 	}
184c6d22b37SRafael J. Wysocki 
185cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
186cbc9ef02SRafael J. Wysocki 		cpuidle_pause_and_lock();
187cbc9ef02SRafael J. Wysocki 		genpd->cpu_data->idle_state->disabled = true;
188cbc9ef02SRafael J. Wysocki 		cpuidle_resume_and_unlock();
189cbc9ef02SRafael J. Wysocki 		goto out;
190cbc9ef02SRafael J. Wysocki 	}
191cbc9ef02SRafael J. Wysocki 
1925063ce15SRafael J. Wysocki 	/*
1935063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
1945063ce15SRafael J. Wysocki 	 * executed, unless one of the masters' .power_on() callbacks fiddles
1955063ce15SRafael J. Wysocki 	 * with it.
1965063ce15SRafael J. Wysocki 	 */
1975063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
1985063ce15SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
19917877eb5SRafael J. Wysocki 		genpd->status = GPD_STATE_WAIT_MASTER;
2003c07cbc4SRafael J. Wysocki 
2015248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
2025248051bSRafael J. Wysocki 
2035063ce15SRafael J. Wysocki 		ret = pm_genpd_poweron(link->master);
2049e08cf42SRafael J. Wysocki 
2059e08cf42SRafael J. Wysocki 		mutex_lock(&genpd->lock);
2069e08cf42SRafael J. Wysocki 
2073f241775SRafael J. Wysocki 		/*
2083f241775SRafael J. Wysocki 		 * The "wait for parent" status is guaranteed not to change
2095063ce15SRafael J. Wysocki 		 * while the master is powering on.
2103f241775SRafael J. Wysocki 		 */
2113f241775SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
2123f241775SRafael J. Wysocki 		wake_up_all(&genpd->status_wait_queue);
2135063ce15SRafael J. Wysocki 		if (ret) {
2145063ce15SRafael J. Wysocki 			genpd_sd_counter_dec(link->master);
2159e08cf42SRafael J. Wysocki 			goto err;
2165248051bSRafael J. Wysocki 		}
2175063ce15SRafael J. Wysocki 	}
2185248051bSRafael J. Wysocki 
2199e08cf42SRafael J. Wysocki 	if (genpd->power_on) {
2200140d8bdSRafael J. Wysocki 		ktime_t time_start = ktime_get();
2210140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
2220140d8bdSRafael J. Wysocki 
223fe202fdeSRafael J. Wysocki 		ret = genpd->power_on(genpd);
2249e08cf42SRafael J. Wysocki 		if (ret)
2259e08cf42SRafael J. Wysocki 			goto err;
2260140d8bdSRafael J. Wysocki 
2270140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
228e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_on_latency_ns) {
2290140d8bdSRafael J. Wysocki 			genpd->power_on_latency_ns = elapsed_ns;
2306ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
231cbc9ef02SRafael J. Wysocki 			genpd_recalc_cpu_exit_latency(genpd);
232e84b2c20SRafael J. Wysocki 			if (genpd->name)
233e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-on latency exceeded, "
234e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
235e84b2c20SRafael J. Wysocki 					elapsed_ns);
236e84b2c20SRafael J. Wysocki 		}
2373c07cbc4SRafael J. Wysocki 	}
2385248051bSRafael J. Wysocki 
239cbc9ef02SRafael J. Wysocki  out:
2409e08cf42SRafael J. Wysocki 	genpd_set_active(genpd);
2419e08cf42SRafael J. Wysocki 
2423f241775SRafael J. Wysocki 	return 0;
2439e08cf42SRafael J. Wysocki 
2449e08cf42SRafael J. Wysocki  err:
2455063ce15SRafael J. Wysocki 	list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
2465063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
2479e08cf42SRafael J. Wysocki 
2483f241775SRafael J. Wysocki 	return ret;
2493f241775SRafael J. Wysocki }
2503f241775SRafael J. Wysocki 
2513f241775SRafael J. Wysocki /**
2525063ce15SRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its masters.
2533f241775SRafael J. Wysocki  * @genpd: PM domain to power up.
2543f241775SRafael J. Wysocki  */
2553f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd)
2563f241775SRafael J. Wysocki {
2573f241775SRafael J. Wysocki 	int ret;
2583f241775SRafael J. Wysocki 
2593f241775SRafael J. Wysocki 	mutex_lock(&genpd->lock);
2603f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
2613f241775SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
2623f241775SRafael J. Wysocki 	return ret;
2635248051bSRafael J. Wysocki }
2645248051bSRafael J. Wysocki 
2655248051bSRafael J. Wysocki #endif /* CONFIG_PM */
2665248051bSRafael J. Wysocki 
2675248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
2685248051bSRafael J. Wysocki 
2698e9afafdSRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev)
2708e9afafdSRafael J. Wysocki {
2718e9afafdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev,
2728e9afafdSRafael J. Wysocki 					save_state_latency_ns, "state save");
2738e9afafdSRafael J. Wysocki }
2748e9afafdSRafael J. Wysocki 
2758e9afafdSRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev)
2768e9afafdSRafael J. Wysocki {
2778e9afafdSRafael J. Wysocki 	return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev,
2788e9afafdSRafael J. Wysocki 					restore_state_latency_ns,
2798e9afafdSRafael J. Wysocki 					"state restore");
2808e9afafdSRafael J. Wysocki }
2818e9afafdSRafael J. Wysocki 
2826ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
2836ff7bb0dSRafael J. Wysocki 				     unsigned long val, void *ptr)
2846ff7bb0dSRafael J. Wysocki {
2856ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
2866ff7bb0dSRafael J. Wysocki 	struct device *dev;
2876ff7bb0dSRafael J. Wysocki 
2886ff7bb0dSRafael J. Wysocki 	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
2896ff7bb0dSRafael J. Wysocki 
2906ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
2916ff7bb0dSRafael J. Wysocki 	dev = gpd_data->base.dev;
2926ff7bb0dSRafael J. Wysocki 	if (!dev) {
2936ff7bb0dSRafael J. Wysocki 		mutex_unlock(&gpd_data->lock);
2946ff7bb0dSRafael J. Wysocki 		return NOTIFY_DONE;
2956ff7bb0dSRafael J. Wysocki 	}
2966ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
2976ff7bb0dSRafael J. Wysocki 
2986ff7bb0dSRafael J. Wysocki 	for (;;) {
2996ff7bb0dSRafael J. Wysocki 		struct generic_pm_domain *genpd;
3006ff7bb0dSRafael J. Wysocki 		struct pm_domain_data *pdd;
3016ff7bb0dSRafael J. Wysocki 
3026ff7bb0dSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
3036ff7bb0dSRafael J. Wysocki 
3046ff7bb0dSRafael J. Wysocki 		pdd = dev->power.subsys_data ?
3056ff7bb0dSRafael J. Wysocki 				dev->power.subsys_data->domain_data : NULL;
3061d5fcfecSRafael J. Wysocki 		if (pdd && pdd->dev) {
3076ff7bb0dSRafael J. Wysocki 			to_gpd_data(pdd)->td.constraint_changed = true;
3086ff7bb0dSRafael J. Wysocki 			genpd = dev_to_genpd(dev);
3096ff7bb0dSRafael J. Wysocki 		} else {
3106ff7bb0dSRafael J. Wysocki 			genpd = ERR_PTR(-ENODATA);
3116ff7bb0dSRafael J. Wysocki 		}
3126ff7bb0dSRafael J. Wysocki 
3136ff7bb0dSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
3146ff7bb0dSRafael J. Wysocki 
3156ff7bb0dSRafael J. Wysocki 		if (!IS_ERR(genpd)) {
3166ff7bb0dSRafael J. Wysocki 			mutex_lock(&genpd->lock);
3176ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
3186ff7bb0dSRafael J. Wysocki 			mutex_unlock(&genpd->lock);
3196ff7bb0dSRafael J. Wysocki 		}
3206ff7bb0dSRafael J. Wysocki 
3216ff7bb0dSRafael J. Wysocki 		dev = dev->parent;
3226ff7bb0dSRafael J. Wysocki 		if (!dev || dev->power.ignore_children)
3236ff7bb0dSRafael J. Wysocki 			break;
3246ff7bb0dSRafael J. Wysocki 	}
3256ff7bb0dSRafael J. Wysocki 
3266ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
3276ff7bb0dSRafael J. Wysocki }
3286ff7bb0dSRafael J. Wysocki 
3295248051bSRafael J. Wysocki /**
330f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
3314605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to save the state of.
332f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
333f721889fSRafael J. Wysocki  */
3344605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd,
335f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
33617b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
337f721889fSRafael J. Wysocki {
338cd0ea672SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
3394605ab65SRafael J. Wysocki 	struct device *dev = pdd->dev;
340f721889fSRafael J. Wysocki 	int ret = 0;
341f721889fSRafael J. Wysocki 
342cd0ea672SRafael J. Wysocki 	if (gpd_data->need_restore)
343f721889fSRafael J. Wysocki 		return 0;
344f721889fSRafael J. Wysocki 
34517b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
34617b75ecaSRafael J. Wysocki 
347d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
348ecf00475SRafael J. Wysocki 	ret = genpd_save_dev(genpd, dev);
349d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
350f721889fSRafael J. Wysocki 
35117b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
35217b75ecaSRafael J. Wysocki 
353f721889fSRafael J. Wysocki 	if (!ret)
354cd0ea672SRafael J. Wysocki 		gpd_data->need_restore = true;
355f721889fSRafael J. Wysocki 
356f721889fSRafael J. Wysocki 	return ret;
357f721889fSRafael J. Wysocki }
358f721889fSRafael J. Wysocki 
359f721889fSRafael J. Wysocki /**
360f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
3614605ab65SRafael J. Wysocki  * @pdd: Domain data of the device to restore the state of.
362f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
363f721889fSRafael J. Wysocki  */
3644605ab65SRafael J. Wysocki static void __pm_genpd_restore_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;
37080de3d7fSRafael J. Wysocki 	bool need_restore = gpd_data->need_restore;
371f721889fSRafael J. Wysocki 
37280de3d7fSRafael J. Wysocki 	gpd_data->need_restore = false;
37317b75ecaSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
37417b75ecaSRafael J. Wysocki 
375d5e4cbfeSRafael J. Wysocki 	genpd_start_dev(genpd, dev);
37680de3d7fSRafael J. Wysocki 	if (need_restore)
377ecf00475SRafael J. Wysocki 		genpd_restore_dev(genpd, dev);
378f721889fSRafael J. Wysocki 
37917b75ecaSRafael J. Wysocki 	mutex_lock(&genpd->lock);
380f721889fSRafael J. Wysocki }
381f721889fSRafael J. Wysocki 
382f721889fSRafael J. Wysocki /**
383c6d22b37SRafael J. Wysocki  * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
384c6d22b37SRafael J. Wysocki  * @genpd: PM domain to check.
385c6d22b37SRafael J. Wysocki  *
386c6d22b37SRafael J. Wysocki  * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
387c6d22b37SRafael J. Wysocki  * a "power off" operation, which means that a "power on" has occured in the
388c6d22b37SRafael J. Wysocki  * meantime, or if its resume_count field is different from zero, which means
389c6d22b37SRafael J. Wysocki  * that one of its devices has been resumed in the meantime.
390c6d22b37SRafael J. Wysocki  */
391c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
392c6d22b37SRafael J. Wysocki {
39317877eb5SRafael J. Wysocki 	return genpd->status == GPD_STATE_WAIT_MASTER
3943f241775SRafael J. Wysocki 		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
395c6d22b37SRafael J. Wysocki }
396c6d22b37SRafael J. Wysocki 
397c6d22b37SRafael J. Wysocki /**
39856375fd4SRafael J. Wysocki  * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
39956375fd4SRafael J. Wysocki  * @genpd: PM domait to power off.
40056375fd4SRafael J. Wysocki  *
40156375fd4SRafael J. Wysocki  * Queue up the execution of pm_genpd_poweroff() unless it's already been done
40256375fd4SRafael J. Wysocki  * before.
40356375fd4SRafael J. Wysocki  */
4040bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
40556375fd4SRafael J. Wysocki {
40656375fd4SRafael J. Wysocki 	if (!work_pending(&genpd->power_off_work))
40756375fd4SRafael J. Wysocki 		queue_work(pm_wq, &genpd->power_off_work);
40856375fd4SRafael J. Wysocki }
40956375fd4SRafael J. Wysocki 
41056375fd4SRafael J. Wysocki /**
411f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
412f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
413f721889fSRafael J. Wysocki  *
414f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
415f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
416f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
417f721889fSRafael J. Wysocki  */
418f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
41917b75ecaSRafael J. Wysocki 	__releases(&genpd->lock) __acquires(&genpd->lock)
420f721889fSRafael J. Wysocki {
4214605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
4225063ce15SRafael J. Wysocki 	struct gpd_link *link;
423f721889fSRafael J. Wysocki 	unsigned int not_suspended;
424c6d22b37SRafael J. Wysocki 	int ret = 0;
425f721889fSRafael J. Wysocki 
426c6d22b37SRafael J. Wysocki  start:
427c6d22b37SRafael J. Wysocki 	/*
428c6d22b37SRafael J. Wysocki 	 * Do not try to power off the domain in the following situations:
429c6d22b37SRafael J. Wysocki 	 * (1) The domain is already in the "power off" state.
4305063ce15SRafael J. Wysocki 	 * (2) The domain is waiting for its master to power up.
431c6d22b37SRafael J. Wysocki 	 * (3) One of the domain's devices is being resumed right now.
4323f241775SRafael J. Wysocki 	 * (4) System suspend is in progress.
433c6d22b37SRafael J. Wysocki 	 */
4343f241775SRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
43517877eb5SRafael J. Wysocki 	    || genpd->status == GPD_STATE_WAIT_MASTER
4363f241775SRafael J. Wysocki 	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
437f721889fSRafael J. Wysocki 		return 0;
438f721889fSRafael J. Wysocki 
439c4bb3160SRafael J. Wysocki 	if (atomic_read(&genpd->sd_count) > 0)
440f721889fSRafael J. Wysocki 		return -EBUSY;
441f721889fSRafael J. Wysocki 
442f721889fSRafael J. Wysocki 	not_suspended = 0;
4434605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
4440aa2a221SRafael J. Wysocki 		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
445dbf37414SRafael J. Wysocki 		    || pdd->dev->power.irq_safe || pdd->dev->power.syscore))
446f721889fSRafael J. Wysocki 			not_suspended++;
447f721889fSRafael J. Wysocki 
448f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
449f721889fSRafael J. Wysocki 		return -EBUSY;
450f721889fSRafael J. Wysocki 
451c6d22b37SRafael J. Wysocki 	if (genpd->poweroff_task) {
452c6d22b37SRafael J. Wysocki 		/*
453c6d22b37SRafael J. Wysocki 		 * Another instance of pm_genpd_poweroff() is executing
454c6d22b37SRafael J. Wysocki 		 * callbacks, so tell it to start over and return.
455c6d22b37SRafael J. Wysocki 		 */
456c6d22b37SRafael J. Wysocki 		genpd->status = GPD_STATE_REPEAT;
457c6d22b37SRafael J. Wysocki 		return 0;
458c6d22b37SRafael J. Wysocki 	}
459c6d22b37SRafael J. Wysocki 
460f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
461f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
462f721889fSRafael J. Wysocki 			return -EAGAIN;
463f721889fSRafael J. Wysocki 	}
464f721889fSRafael J. Wysocki 
46517b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
466c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = current;
46717b75ecaSRafael J. Wysocki 
4684605ab65SRafael J. Wysocki 	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
4693c07cbc4SRafael J. Wysocki 		ret = atomic_read(&genpd->sd_count) == 0 ?
4704605ab65SRafael J. Wysocki 			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
4713f241775SRafael J. Wysocki 
4723f241775SRafael J. Wysocki 		if (genpd_abort_poweroff(genpd))
4733f241775SRafael J. Wysocki 			goto out;
4743f241775SRafael J. Wysocki 
475697a7f37SRafael J. Wysocki 		if (ret) {
476697a7f37SRafael J. Wysocki 			genpd_set_active(genpd);
477697a7f37SRafael J. Wysocki 			goto out;
478697a7f37SRafael J. Wysocki 		}
479f721889fSRafael J. Wysocki 
480c6d22b37SRafael J. Wysocki 		if (genpd->status == GPD_STATE_REPEAT) {
481c6d22b37SRafael J. Wysocki 			genpd->poweroff_task = NULL;
482c6d22b37SRafael J. Wysocki 			goto start;
483c6d22b37SRafael J. Wysocki 		}
484c6d22b37SRafael J. Wysocki 	}
48517b75ecaSRafael J. Wysocki 
486cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
487cbc9ef02SRafael J. Wysocki 		/*
488cbc9ef02SRafael J. Wysocki 		 * If cpu_data is set, cpuidle should turn the domain off when
489cbc9ef02SRafael J. Wysocki 		 * the CPU in it is idle.  In that case we don't decrement the
490cbc9ef02SRafael J. Wysocki 		 * subdomain counts of the master domains, so that power is not
491cbc9ef02SRafael J. Wysocki 		 * removed from the current domain prematurely as a result of
492cbc9ef02SRafael J. Wysocki 		 * cutting off the masters' power.
493cbc9ef02SRafael J. Wysocki 		 */
494cbc9ef02SRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
495cbc9ef02SRafael J. Wysocki 		cpuidle_pause_and_lock();
496cbc9ef02SRafael J. Wysocki 		genpd->cpu_data->idle_state->disabled = false;
497cbc9ef02SRafael J. Wysocki 		cpuidle_resume_and_unlock();
498cbc9ef02SRafael J. Wysocki 		goto out;
499cbc9ef02SRafael J. Wysocki 	}
500cbc9ef02SRafael J. Wysocki 
5013c07cbc4SRafael J. Wysocki 	if (genpd->power_off) {
5020140d8bdSRafael J. Wysocki 		ktime_t time_start;
5030140d8bdSRafael J. Wysocki 		s64 elapsed_ns;
5040140d8bdSRafael J. Wysocki 
5053c07cbc4SRafael J. Wysocki 		if (atomic_read(&genpd->sd_count) > 0) {
5063c07cbc4SRafael J. Wysocki 			ret = -EBUSY;
507c6d22b37SRafael J. Wysocki 			goto out;
508c6d22b37SRafael J. Wysocki 		}
50917b75ecaSRafael J. Wysocki 
5100140d8bdSRafael J. Wysocki 		time_start = ktime_get();
5110140d8bdSRafael J. Wysocki 
5123c07cbc4SRafael J. Wysocki 		/*
5135063ce15SRafael J. Wysocki 		 * If sd_count > 0 at this point, one of the subdomains hasn't
5145063ce15SRafael J. Wysocki 		 * managed to call pm_genpd_poweron() for the master yet after
5153c07cbc4SRafael J. Wysocki 		 * incrementing it.  In that case pm_genpd_poweron() will wait
5163c07cbc4SRafael J. Wysocki 		 * for us to drop the lock, so we can call .power_off() and let
5173c07cbc4SRafael J. Wysocki 		 * the pm_genpd_poweron() restore power for us (this shouldn't
5183c07cbc4SRafael J. Wysocki 		 * happen very often).
5193c07cbc4SRafael J. Wysocki 		 */
520d2805402SRafael J. Wysocki 		ret = genpd->power_off(genpd);
521d2805402SRafael J. Wysocki 		if (ret == -EBUSY) {
522d2805402SRafael J. Wysocki 			genpd_set_active(genpd);
523d2805402SRafael J. Wysocki 			goto out;
524d2805402SRafael J. Wysocki 		}
5250140d8bdSRafael J. Wysocki 
5260140d8bdSRafael J. Wysocki 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
527e84b2c20SRafael J. Wysocki 		if (elapsed_ns > genpd->power_off_latency_ns) {
5280140d8bdSRafael J. Wysocki 			genpd->power_off_latency_ns = elapsed_ns;
5296ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
530e84b2c20SRafael J. Wysocki 			if (genpd->name)
531e84b2c20SRafael J. Wysocki 				pr_warning("%s: Power-off latency exceeded, "
532e84b2c20SRafael J. Wysocki 					"new value %lld ns\n", genpd->name,
533e84b2c20SRafael J. Wysocki 					elapsed_ns);
534e84b2c20SRafael J. Wysocki 		}
535d2805402SRafael J. Wysocki 	}
536f721889fSRafael J. Wysocki 
53717b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
538221e9b58SRafael J. Wysocki 
5395063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
5405063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
5415063ce15SRafael J. Wysocki 		genpd_queue_power_off_work(link->master);
5425063ce15SRafael J. Wysocki 	}
54317b75ecaSRafael J. Wysocki 
544c6d22b37SRafael J. Wysocki  out:
545c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
546c6d22b37SRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
547c6d22b37SRafael J. Wysocki 	return ret;
548f721889fSRafael J. Wysocki }
549f721889fSRafael J. Wysocki 
550f721889fSRafael J. Wysocki /**
551f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
552f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
553f721889fSRafael J. Wysocki  */
554f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
555f721889fSRafael J. Wysocki {
556f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
557f721889fSRafael J. Wysocki 
558f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
559f721889fSRafael J. Wysocki 
56017b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
561f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
56217b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
563f721889fSRafael J. Wysocki }
564f721889fSRafael J. Wysocki 
565f721889fSRafael J. Wysocki /**
566f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
567f721889fSRafael J. Wysocki  * @dev: Device to suspend.
568f721889fSRafael J. Wysocki  *
569f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
570f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
571f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
572f721889fSRafael J. Wysocki  */
573f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
574f721889fSRafael J. Wysocki {
575f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
576b02c999aSRafael J. Wysocki 	bool (*stop_ok)(struct device *__dev);
577d5e4cbfeSRafael J. Wysocki 	int ret;
578f721889fSRafael J. Wysocki 
579f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
580f721889fSRafael J. Wysocki 
5815248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
5825248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
583f721889fSRafael J. Wysocki 		return -EINVAL;
584f721889fSRafael J. Wysocki 
5850aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
5860aa2a221SRafael J. Wysocki 
587b02c999aSRafael J. Wysocki 	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
588b02c999aSRafael J. Wysocki 	if (stop_ok && !stop_ok(dev))
589b02c999aSRafael J. Wysocki 		return -EBUSY;
590b02c999aSRafael J. Wysocki 
591d5e4cbfeSRafael J. Wysocki 	ret = genpd_stop_dev(genpd, dev);
592f721889fSRafael J. Wysocki 	if (ret)
59317b75ecaSRafael J. Wysocki 		return ret;
59417b75ecaSRafael J. Wysocki 
5950aa2a221SRafael J. Wysocki 	/*
5960aa2a221SRafael J. Wysocki 	 * If power.irq_safe is set, this routine will be run with interrupts
5970aa2a221SRafael J. Wysocki 	 * off, so it can't use mutexes.
5980aa2a221SRafael J. Wysocki 	 */
5990aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
6000aa2a221SRafael J. Wysocki 		return 0;
6010aa2a221SRafael J. Wysocki 
602c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
603f721889fSRafael J. Wysocki 	genpd->in_progress++;
604f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
605f721889fSRafael J. Wysocki 	genpd->in_progress--;
606c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
607f721889fSRafael J. Wysocki 
608f721889fSRafael J. Wysocki 	return 0;
609f721889fSRafael J. Wysocki }
610f721889fSRafael J. Wysocki 
611f721889fSRafael J. Wysocki /**
612f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
613f721889fSRafael J. Wysocki  * @dev: Device to resume.
614f721889fSRafael J. Wysocki  *
615f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
616f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
617f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
618f721889fSRafael J. Wysocki  */
619f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
620f721889fSRafael J. Wysocki {
621f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
622c6d22b37SRafael J. Wysocki 	DEFINE_WAIT(wait);
623f721889fSRafael J. Wysocki 	int ret;
624f721889fSRafael J. Wysocki 
625f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
626f721889fSRafael J. Wysocki 
6275248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
6285248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
629f721889fSRafael J. Wysocki 		return -EINVAL;
630f721889fSRafael J. Wysocki 
6310aa2a221SRafael J. Wysocki 	might_sleep_if(!genpd->dev_irq_safe);
6320aa2a221SRafael J. Wysocki 
6330aa2a221SRafael J. Wysocki 	/* If power.irq_safe, the PM domain is never powered off. */
6340aa2a221SRafael J. Wysocki 	if (dev->power.irq_safe)
635e2e3e4e5SRafael J. Wysocki 		return genpd_start_dev_no_timing(genpd, dev);
6360aa2a221SRafael J. Wysocki 
637c6d22b37SRafael J. Wysocki 	mutex_lock(&genpd->lock);
6383f241775SRafael J. Wysocki 	ret = __pm_genpd_poweron(genpd);
6393f241775SRafael J. Wysocki 	if (ret) {
6403f241775SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
6413f241775SRafael J. Wysocki 		return ret;
6423f241775SRafael J. Wysocki 	}
64317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_BUSY;
644c6d22b37SRafael J. Wysocki 	genpd->resume_count++;
645c6d22b37SRafael J. Wysocki 	for (;;) {
646c6d22b37SRafael J. Wysocki 		prepare_to_wait(&genpd->status_wait_queue, &wait,
647c6d22b37SRafael J. Wysocki 				TASK_UNINTERRUPTIBLE);
648c6d22b37SRafael J. Wysocki 		/*
649c6d22b37SRafael J. Wysocki 		 * If current is the powering off task, we have been called
650c6d22b37SRafael J. Wysocki 		 * reentrantly from one of the device callbacks, so we should
651c6d22b37SRafael J. Wysocki 		 * not wait.
652c6d22b37SRafael J. Wysocki 		 */
653c6d22b37SRafael J. Wysocki 		if (!genpd->poweroff_task || genpd->poweroff_task == current)
654c6d22b37SRafael J. Wysocki 			break;
655c6d22b37SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
656c6d22b37SRafael J. Wysocki 
657c6d22b37SRafael J. Wysocki 		schedule();
658c6d22b37SRafael J. Wysocki 
659c6d22b37SRafael J. Wysocki 		mutex_lock(&genpd->lock);
660c6d22b37SRafael J. Wysocki 	}
661c6d22b37SRafael J. Wysocki 	finish_wait(&genpd->status_wait_queue, &wait);
662cd0ea672SRafael J. Wysocki 	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
663c6d22b37SRafael J. Wysocki 	genpd->resume_count--;
664c6d22b37SRafael J. Wysocki 	genpd_set_active(genpd);
66517b75ecaSRafael J. Wysocki 	wake_up_all(&genpd->status_wait_queue);
666c6d22b37SRafael J. Wysocki 	mutex_unlock(&genpd->lock);
66717b75ecaSRafael J. Wysocki 
668f721889fSRafael J. Wysocki 	return 0;
669f721889fSRafael J. Wysocki }
670f721889fSRafael J. Wysocki 
67117f2ae7fSRafael J. Wysocki /**
67217f2ae7fSRafael J. Wysocki  * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
67317f2ae7fSRafael J. Wysocki  */
67417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void)
67517f2ae7fSRafael J. Wysocki {
67617f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
67717f2ae7fSRafael J. Wysocki 
67817f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
67917f2ae7fSRafael J. Wysocki 
68017f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
68117f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
68217f2ae7fSRafael J. Wysocki 
68317f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
68417f2ae7fSRafael J. Wysocki }
68517f2ae7fSRafael J. Wysocki 
686f721889fSRafael J. Wysocki #else
687f721889fSRafael J. Wysocki 
6886ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
6896ff7bb0dSRafael J. Wysocki 					    unsigned long val, void *ptr)
6906ff7bb0dSRafael J. Wysocki {
6916ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
6926ff7bb0dSRafael J. Wysocki }
6936ff7bb0dSRafael J. Wysocki 
694f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
695f721889fSRafael J. Wysocki 
696f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
697f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
698f721889fSRafael J. Wysocki 
699f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
700f721889fSRafael J. Wysocki 
701596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
702596ba34bSRafael J. Wysocki 
70377f827deSRafael J. Wysocki /**
70477f827deSRafael J. Wysocki  * pm_genpd_present - Check if the given PM domain has been initialized.
70577f827deSRafael J. Wysocki  * @genpd: PM domain to check.
70677f827deSRafael J. Wysocki  */
70777f827deSRafael J. Wysocki static bool pm_genpd_present(struct generic_pm_domain *genpd)
70877f827deSRafael J. Wysocki {
70977f827deSRafael J. Wysocki 	struct generic_pm_domain *gpd;
71077f827deSRafael J. Wysocki 
71177f827deSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
71277f827deSRafael J. Wysocki 		return false;
71377f827deSRafael J. Wysocki 
71477f827deSRafael J. Wysocki 	list_for_each_entry(gpd, &gpd_list, gpd_list_node)
71577f827deSRafael J. Wysocki 		if (gpd == genpd)
71677f827deSRafael J. Wysocki 			return true;
71777f827deSRafael J. Wysocki 
71877f827deSRafael J. Wysocki 	return false;
71977f827deSRafael J. Wysocki }
72077f827deSRafael J. Wysocki 
721d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
722d5e4cbfeSRafael J. Wysocki 				    struct device *dev)
723d5e4cbfeSRafael J. Wysocki {
724d5e4cbfeSRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
725d5e4cbfeSRafael J. Wysocki }
726d5e4cbfeSRafael J. Wysocki 
727d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
728d23b9b00SRafael J. Wysocki {
729d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
730d23b9b00SRafael J. Wysocki }
731d23b9b00SRafael J. Wysocki 
732d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
733d23b9b00SRafael J. Wysocki {
734d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
735d23b9b00SRafael J. Wysocki }
736d23b9b00SRafael J. Wysocki 
737d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
738d23b9b00SRafael J. Wysocki {
739d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
740d23b9b00SRafael J. Wysocki }
741d23b9b00SRafael J. Wysocki 
742d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
743d23b9b00SRafael J. Wysocki {
744d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
745d23b9b00SRafael J. Wysocki }
746d23b9b00SRafael J. Wysocki 
747d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
748d23b9b00SRafael J. Wysocki {
749d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
750d23b9b00SRafael J. Wysocki }
751d23b9b00SRafael J. Wysocki 
752d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
753d23b9b00SRafael J. Wysocki {
754d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
755d23b9b00SRafael J. Wysocki }
756d23b9b00SRafael J. Wysocki 
757d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
758d23b9b00SRafael J. Wysocki {
759d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
760d23b9b00SRafael J. Wysocki }
761d23b9b00SRafael J. Wysocki 
762d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
763d23b9b00SRafael J. Wysocki {
764d23b9b00SRafael J. Wysocki 	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
765d23b9b00SRafael J. Wysocki }
766d23b9b00SRafael J. Wysocki 
767596ba34bSRafael J. Wysocki /**
7685063ce15SRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
769596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
770596ba34bSRafael J. Wysocki  *
771596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
7725063ce15SRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its masters.
773596ba34bSRafael J. Wysocki  *
77477f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
77577f827deSRafael J. Wysocki  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
77677f827deSRafael J. Wysocki  * executed sequentially, so it is guaranteed that it will never run twice in
77777f827deSRafael J. Wysocki  * parallel).
778596ba34bSRafael J. Wysocki  */
779596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
780596ba34bSRafael J. Wysocki {
7815063ce15SRafael J. Wysocki 	struct gpd_link *link;
782596ba34bSRafael J. Wysocki 
78317b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF)
784596ba34bSRafael J. Wysocki 		return;
785596ba34bSRafael J. Wysocki 
786c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
787c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
788596ba34bSRafael J. Wysocki 		return;
789596ba34bSRafael J. Wysocki 
790596ba34bSRafael J. Wysocki 	if (genpd->power_off)
791596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
792596ba34bSRafael J. Wysocki 
79317b75ecaSRafael J. Wysocki 	genpd->status = GPD_STATE_POWER_OFF;
7945063ce15SRafael J. Wysocki 
7955063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
7965063ce15SRafael J. Wysocki 		genpd_sd_counter_dec(link->master);
7975063ce15SRafael J. Wysocki 		pm_genpd_sync_poweroff(link->master);
798596ba34bSRafael J. Wysocki 	}
799596ba34bSRafael J. Wysocki }
800596ba34bSRafael J. Wysocki 
801596ba34bSRafael J. Wysocki /**
802802d8b49SRafael J. Wysocki  * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
803802d8b49SRafael J. Wysocki  * @genpd: PM domain to power on.
804802d8b49SRafael J. Wysocki  *
80577f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
80677f827deSRafael J. Wysocki  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
80777f827deSRafael J. Wysocki  * executed sequentially, so it is guaranteed that it will never run twice in
80877f827deSRafael J. Wysocki  * parallel).
809802d8b49SRafael J. Wysocki  */
810802d8b49SRafael J. Wysocki static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
811802d8b49SRafael J. Wysocki {
812802d8b49SRafael J. Wysocki 	struct gpd_link *link;
813802d8b49SRafael J. Wysocki 
814802d8b49SRafael J. Wysocki 	if (genpd->status != GPD_STATE_POWER_OFF)
815802d8b49SRafael J. Wysocki 		return;
816802d8b49SRafael J. Wysocki 
817802d8b49SRafael J. Wysocki 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
818802d8b49SRafael J. Wysocki 		pm_genpd_sync_poweron(link->master);
819802d8b49SRafael J. Wysocki 		genpd_sd_counter_inc(link->master);
820802d8b49SRafael J. Wysocki 	}
821802d8b49SRafael J. Wysocki 
822802d8b49SRafael J. Wysocki 	if (genpd->power_on)
823802d8b49SRafael J. Wysocki 		genpd->power_on(genpd);
824802d8b49SRafael J. Wysocki 
825802d8b49SRafael J. Wysocki 	genpd->status = GPD_STATE_ACTIVE;
826802d8b49SRafael J. Wysocki }
827802d8b49SRafael J. Wysocki 
828802d8b49SRafael J. Wysocki /**
8294ecd6e65SRafael J. Wysocki  * resume_needed - Check whether to resume a device before system suspend.
8304ecd6e65SRafael J. Wysocki  * @dev: Device to check.
8314ecd6e65SRafael J. Wysocki  * @genpd: PM domain the device belongs to.
8324ecd6e65SRafael J. Wysocki  *
8334ecd6e65SRafael J. Wysocki  * There are two cases in which a device that can wake up the system from sleep
8344ecd6e65SRafael J. Wysocki  * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
8354ecd6e65SRafael J. Wysocki  * to wake up the system and it has to remain active for this purpose while the
8364ecd6e65SRafael J. Wysocki  * system is in the sleep state and (2) if the device is not enabled to wake up
8374ecd6e65SRafael J. Wysocki  * the system from sleep states and it generally doesn't generate wakeup signals
8384ecd6e65SRafael J. Wysocki  * by itself (those signals are generated on its behalf by other parts of the
8394ecd6e65SRafael J. Wysocki  * system).  In the latter case it may be necessary to reconfigure the device's
8404ecd6e65SRafael J. Wysocki  * wakeup settings during system suspend, because it may have been set up to
8414ecd6e65SRafael J. Wysocki  * signal remote wakeup from the system's working state as needed by runtime PM.
8424ecd6e65SRafael J. Wysocki  * Return 'true' in either of the above cases.
8434ecd6e65SRafael J. Wysocki  */
8444ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
8454ecd6e65SRafael J. Wysocki {
8464ecd6e65SRafael J. Wysocki 	bool active_wakeup;
8474ecd6e65SRafael J. Wysocki 
8484ecd6e65SRafael J. Wysocki 	if (!device_can_wakeup(dev))
8494ecd6e65SRafael J. Wysocki 		return false;
8504ecd6e65SRafael J. Wysocki 
851d5e4cbfeSRafael J. Wysocki 	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
8524ecd6e65SRafael J. Wysocki 	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
8534ecd6e65SRafael J. Wysocki }
8544ecd6e65SRafael J. Wysocki 
8554ecd6e65SRafael J. Wysocki /**
856596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
857596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
858596ba34bSRafael J. Wysocki  *
859596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
860596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
861596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
862596ba34bSRafael J. Wysocki  * consisting of I/O devices.
863596ba34bSRafael J. Wysocki  */
864596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
865596ba34bSRafael J. Wysocki {
866596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
867b6c10c84SRafael J. Wysocki 	int ret;
868596ba34bSRafael J. Wysocki 
869596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
870596ba34bSRafael J. Wysocki 
871596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
872596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
873596ba34bSRafael J. Wysocki 		return -EINVAL;
874596ba34bSRafael J. Wysocki 
87517b75ecaSRafael J. Wysocki 	/*
87617b75ecaSRafael J. Wysocki 	 * If a wakeup request is pending for the device, it should be woken up
87717b75ecaSRafael J. Wysocki 	 * at this point and a system wakeup event should be reported if it's
87817b75ecaSRafael J. Wysocki 	 * set up to wake up the system from sleep states.
87917b75ecaSRafael J. Wysocki 	 */
88017b75ecaSRafael J. Wysocki 	pm_runtime_get_noresume(dev);
88117b75ecaSRafael J. Wysocki 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
88217b75ecaSRafael J. Wysocki 		pm_wakeup_event(dev, 0);
88317b75ecaSRafael J. Wysocki 
88417b75ecaSRafael J. Wysocki 	if (pm_wakeup_pending()) {
88517b75ecaSRafael J. Wysocki 		pm_runtime_put_sync(dev);
88617b75ecaSRafael J. Wysocki 		return -EBUSY;
88717b75ecaSRafael J. Wysocki 	}
88817b75ecaSRafael J. Wysocki 
8894ecd6e65SRafael J. Wysocki 	if (resume_needed(dev, genpd))
8904ecd6e65SRafael J. Wysocki 		pm_runtime_resume(dev);
8914ecd6e65SRafael J. Wysocki 
89217b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
893596ba34bSRafael J. Wysocki 
89465533bbfSRafael J. Wysocki 	if (genpd->prepared_count++ == 0) {
89565533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
89617b75ecaSRafael J. Wysocki 		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
89765533bbfSRafael J. Wysocki 	}
89817b75ecaSRafael J. Wysocki 
89917b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
900596ba34bSRafael J. Wysocki 
901596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
90217b75ecaSRafael J. Wysocki 		pm_runtime_put_noidle(dev);
903596ba34bSRafael J. Wysocki 		return 0;
904596ba34bSRafael J. Wysocki 	}
905596ba34bSRafael J. Wysocki 
906596ba34bSRafael J. Wysocki 	/*
90717b75ecaSRafael J. Wysocki 	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
90817b75ecaSRafael J. Wysocki 	 * so pm_genpd_poweron() will return immediately, but if the device
909d5e4cbfeSRafael J. Wysocki 	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
91017b75ecaSRafael J. Wysocki 	 * to make it operational.
911596ba34bSRafael J. Wysocki 	 */
91217b75ecaSRafael J. Wysocki 	pm_runtime_resume(dev);
913596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
914596ba34bSRafael J. Wysocki 
915b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
916b6c10c84SRafael J. Wysocki 	if (ret) {
917b6c10c84SRafael J. Wysocki 		mutex_lock(&genpd->lock);
918b6c10c84SRafael J. Wysocki 
919b6c10c84SRafael J. Wysocki 		if (--genpd->prepared_count == 0)
920b6c10c84SRafael J. Wysocki 			genpd->suspend_power_off = false;
921b6c10c84SRafael J. Wysocki 
922b6c10c84SRafael J. Wysocki 		mutex_unlock(&genpd->lock);
92317b75ecaSRafael J. Wysocki 		pm_runtime_enable(dev);
924b6c10c84SRafael J. Wysocki 	}
92517b75ecaSRafael J. Wysocki 
92617b75ecaSRafael J. Wysocki 	pm_runtime_put_sync(dev);
927b6c10c84SRafael J. Wysocki 	return ret;
928596ba34bSRafael J. Wysocki }
929596ba34bSRafael J. Wysocki 
930596ba34bSRafael J. Wysocki /**
931596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
932596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
933596ba34bSRafael J. Wysocki  *
934596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
935596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
936596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
937596ba34bSRafael J. Wysocki  */
938596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
939596ba34bSRafael J. Wysocki {
940596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
941596ba34bSRafael J. Wysocki 
942596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
943596ba34bSRafael J. Wysocki 
944596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
945596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
946596ba34bSRafael J. Wysocki 		return -EINVAL;
947596ba34bSRafael J. Wysocki 
948d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
949596ba34bSRafael J. Wysocki }
950596ba34bSRafael J. Wysocki 
951596ba34bSRafael J. Wysocki /**
9520496c8aeSRafael J. Wysocki  * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
953596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
954596ba34bSRafael J. Wysocki  *
955596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
956596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
957596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
958596ba34bSRafael J. Wysocki  */
9590496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev)
960596ba34bSRafael J. Wysocki {
961596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
962596ba34bSRafael J. Wysocki 
963596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
964596ba34bSRafael J. Wysocki 
965596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
966596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
967596ba34bSRafael J. Wysocki 		return -EINVAL;
968596ba34bSRafael J. Wysocki 
9690496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
9700496c8aeSRafael J. Wysocki }
971596ba34bSRafael J. Wysocki 
9720496c8aeSRafael J. Wysocki /**
9730496c8aeSRafael J. Wysocki  * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
9740496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
9750496c8aeSRafael J. Wysocki  *
9760496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
9770496c8aeSRafael J. Wysocki  * been stopped.
9780496c8aeSRafael J. Wysocki  */
9790496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
9800496c8aeSRafael J. Wysocki {
9810496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
982596ba34bSRafael J. Wysocki 
9830496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
9840496c8aeSRafael J. Wysocki 
9850496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9860496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
9870496c8aeSRafael J. Wysocki 		return -EINVAL;
9880496c8aeSRafael J. Wysocki 
989dbf37414SRafael J. Wysocki 	if (genpd->suspend_power_off
9900496c8aeSRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
991d4f2d87aSRafael J. Wysocki 		return 0;
992d4f2d87aSRafael J. Wysocki 
993d5e4cbfeSRafael J. Wysocki 	genpd_stop_dev(genpd, dev);
994596ba34bSRafael J. Wysocki 
995596ba34bSRafael J. Wysocki 	/*
996596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
997596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
998596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
999596ba34bSRafael J. Wysocki 	 */
1000596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
1001596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
1002596ba34bSRafael J. Wysocki 
1003596ba34bSRafael J. Wysocki 	return 0;
1004596ba34bSRafael J. Wysocki }
1005596ba34bSRafael J. Wysocki 
1006596ba34bSRafael J. Wysocki /**
10070496c8aeSRafael J. Wysocki  * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
1008596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1009596ba34bSRafael J. Wysocki  *
10100496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
1011596ba34bSRafael J. Wysocki  */
1012596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
1013596ba34bSRafael J. Wysocki {
1014596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1015596ba34bSRafael J. Wysocki 
1016596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1017596ba34bSRafael J. Wysocki 
1018596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1019596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1020596ba34bSRafael J. Wysocki 		return -EINVAL;
1021596ba34bSRafael J. Wysocki 
1022dbf37414SRafael J. Wysocki 	if (genpd->suspend_power_off
1023cc85b207SRafael J. Wysocki 	    || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
1024596ba34bSRafael J. Wysocki 		return 0;
1025596ba34bSRafael J. Wysocki 
1026596ba34bSRafael J. Wysocki 	/*
1027596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1028596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1029596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
1030596ba34bSRafael J. Wysocki 	 */
1031802d8b49SRafael J. Wysocki 	pm_genpd_sync_poweron(genpd);
1032596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
1033596ba34bSRafael J. Wysocki 
10340496c8aeSRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1035596ba34bSRafael J. Wysocki }
1036596ba34bSRafael J. Wysocki 
1037596ba34bSRafael J. Wysocki /**
10380496c8aeSRafael J. Wysocki  * pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
10390496c8aeSRafael J. Wysocki  * @dev: Device to resume.
10400496c8aeSRafael J. Wysocki  *
10410496c8aeSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
10420496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
10430496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
10440496c8aeSRafael J. Wysocki  * devices.
10450496c8aeSRafael J. Wysocki  */
10460496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev)
10470496c8aeSRafael J. Wysocki {
10480496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
10490496c8aeSRafael J. Wysocki 
10500496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
10510496c8aeSRafael J. Wysocki 
10520496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10530496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
10540496c8aeSRafael J. Wysocki 		return -EINVAL;
10550496c8aeSRafael J. Wysocki 
10560496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
10570496c8aeSRafael J. Wysocki }
10580496c8aeSRafael J. Wysocki 
10590496c8aeSRafael J. Wysocki /**
10600496c8aeSRafael J. Wysocki  * pm_genpd_resume - Resume of device in an I/O PM domain.
1061596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1062596ba34bSRafael J. Wysocki  *
1063596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
1064596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1065596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1066596ba34bSRafael J. Wysocki  */
1067596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
1068596ba34bSRafael J. Wysocki {
1069596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1070596ba34bSRafael J. Wysocki 
1071596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1072596ba34bSRafael J. Wysocki 
1073596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1074596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1075596ba34bSRafael J. Wysocki 		return -EINVAL;
1076596ba34bSRafael J. Wysocki 
1077d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
1078596ba34bSRafael J. Wysocki }
1079596ba34bSRafael J. Wysocki 
1080596ba34bSRafael J. Wysocki /**
10810496c8aeSRafael J. Wysocki  * pm_genpd_freeze - Freezing a device in an I/O PM domain.
1082596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1083596ba34bSRafael J. Wysocki  *
1084596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
1085596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1086596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1087596ba34bSRafael J. Wysocki  */
1088596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
1089596ba34bSRafael J. Wysocki {
1090596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1091596ba34bSRafael J. Wysocki 
1092596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1093596ba34bSRafael J. Wysocki 
1094596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1095596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1096596ba34bSRafael J. Wysocki 		return -EINVAL;
1097596ba34bSRafael J. Wysocki 
1098d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
1099596ba34bSRafael J. Wysocki }
1100596ba34bSRafael J. Wysocki 
1101596ba34bSRafael J. Wysocki /**
11020496c8aeSRafael J. Wysocki  * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
11030496c8aeSRafael J. Wysocki  * @dev: Device to freeze.
11040496c8aeSRafael J. Wysocki  *
11050496c8aeSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
11060496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
11070496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
11080496c8aeSRafael J. Wysocki  * devices.
11090496c8aeSRafael J. Wysocki  */
11100496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev)
11110496c8aeSRafael J. Wysocki {
11120496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
11130496c8aeSRafael J. Wysocki 
11140496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
11150496c8aeSRafael J. Wysocki 
11160496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
11170496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
11180496c8aeSRafael J. Wysocki 		return -EINVAL;
11190496c8aeSRafael J. Wysocki 
11200496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
11210496c8aeSRafael J. Wysocki }
11220496c8aeSRafael J. Wysocki 
11230496c8aeSRafael J. Wysocki /**
11240496c8aeSRafael J. Wysocki  * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1125596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1126596ba34bSRafael J. Wysocki  *
1127596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1128596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1129596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1130596ba34bSRafael J. Wysocki  * devices.
1131596ba34bSRafael J. Wysocki  */
1132596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
1133596ba34bSRafael J. Wysocki {
1134596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1135596ba34bSRafael J. Wysocki 
1136596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1137596ba34bSRafael J. Wysocki 
1138596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1139596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1140596ba34bSRafael J. Wysocki 		return -EINVAL;
1141596ba34bSRafael J. Wysocki 
1142dbf37414SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
1143596ba34bSRafael J. Wysocki }
1144596ba34bSRafael J. Wysocki 
1145596ba34bSRafael J. Wysocki /**
11460496c8aeSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1147596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1148596ba34bSRafael J. Wysocki  *
11490496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
11500496c8aeSRafael J. Wysocki  * before the system transition.
1151596ba34bSRafael J. Wysocki  */
1152596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
1153596ba34bSRafael J. Wysocki {
1154596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1155596ba34bSRafael J. Wysocki 
1156596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1157596ba34bSRafael J. Wysocki 
1158596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1159596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1160596ba34bSRafael J. Wysocki 		return -EINVAL;
1161596ba34bSRafael J. Wysocki 
1162dbf37414SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
11630496c8aeSRafael J. Wysocki }
1164596ba34bSRafael J. Wysocki 
11650496c8aeSRafael J. Wysocki /**
11660496c8aeSRafael J. Wysocki  * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
11670496c8aeSRafael J. Wysocki  * @dev: Device to thaw.
11680496c8aeSRafael J. Wysocki  *
11690496c8aeSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
11700496c8aeSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
11710496c8aeSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
11720496c8aeSRafael J. Wysocki  * devices.
11730496c8aeSRafael J. Wysocki  */
11740496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev)
11750496c8aeSRafael J. Wysocki {
11760496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1177596ba34bSRafael J. Wysocki 
11780496c8aeSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
11790496c8aeSRafael J. Wysocki 
11800496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
11810496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
11820496c8aeSRafael J. Wysocki 		return -EINVAL;
11830496c8aeSRafael J. Wysocki 
11840496c8aeSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
1185596ba34bSRafael J. Wysocki }
1186596ba34bSRafael J. Wysocki 
1187596ba34bSRafael J. Wysocki /**
1188596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
1189596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1190596ba34bSRafael J. Wysocki  *
1191596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
1192596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1193596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1194596ba34bSRafael J. Wysocki  */
1195596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
1196596ba34bSRafael J. Wysocki {
1197596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1198596ba34bSRafael J. Wysocki 
1199596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1200596ba34bSRafael J. Wysocki 
1201596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1202596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1203596ba34bSRafael J. Wysocki 		return -EINVAL;
1204596ba34bSRafael J. Wysocki 
1205d23b9b00SRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
1206596ba34bSRafael J. Wysocki }
1207596ba34bSRafael J. Wysocki 
1208596ba34bSRafael J. Wysocki /**
12090496c8aeSRafael J. Wysocki  * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1210596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1211596ba34bSRafael J. Wysocki  *
12120496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
12130496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1214596ba34bSRafael J. Wysocki  */
1215596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
1216596ba34bSRafael J. Wysocki {
1217596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1218596ba34bSRafael J. Wysocki 
1219596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1220596ba34bSRafael J. Wysocki 
1221596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1222596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1223596ba34bSRafael J. Wysocki 		return -EINVAL;
1224596ba34bSRafael J. Wysocki 
1225596ba34bSRafael J. Wysocki 	/*
1226596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
1227596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
1228596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
122965533bbfSRafael J. Wysocki 	 *
123065533bbfSRafael J. Wysocki 	 * At this point suspended_count == 0 means we are being run for the
123165533bbfSRafael J. Wysocki 	 * first time for the given domain in the present cycle.
123265533bbfSRafael J. Wysocki 	 */
123365533bbfSRafael J. Wysocki 	if (genpd->suspended_count++ == 0) {
123465533bbfSRafael J. Wysocki 		/*
123565533bbfSRafael J. Wysocki 		 * The boot kernel might put the domain into arbitrary state,
1236802d8b49SRafael J. Wysocki 		 * so make it appear as powered off to pm_genpd_sync_poweron(),
1237802d8b49SRafael J. Wysocki 		 * so that it tries to power it on in case it was really off.
1238596ba34bSRafael J. Wysocki 		 */
123917b75ecaSRafael J. Wysocki 		genpd->status = GPD_STATE_POWER_OFF;
1240596ba34bSRafael J. Wysocki 		if (genpd->suspend_power_off) {
1241596ba34bSRafael J. Wysocki 			/*
124265533bbfSRafael J. Wysocki 			 * If the domain was off before the hibernation, make
124365533bbfSRafael J. Wysocki 			 * sure it will be off going forward.
1244596ba34bSRafael J. Wysocki 			 */
1245596ba34bSRafael J. Wysocki 			if (genpd->power_off)
1246596ba34bSRafael J. Wysocki 				genpd->power_off(genpd);
124765533bbfSRafael J. Wysocki 
1248596ba34bSRafael J. Wysocki 			return 0;
1249596ba34bSRafael J. Wysocki 		}
125065533bbfSRafael J. Wysocki 	}
1251596ba34bSRafael J. Wysocki 
125218dd2eceSRafael J. Wysocki 	if (genpd->suspend_power_off)
125318dd2eceSRafael J. Wysocki 		return 0;
125418dd2eceSRafael J. Wysocki 
1255802d8b49SRafael J. Wysocki 	pm_genpd_sync_poweron(genpd);
1256596ba34bSRafael J. Wysocki 
1257dbf37414SRafael J. Wysocki 	return genpd_start_dev(genpd, dev);
1258596ba34bSRafael J. Wysocki }
1259596ba34bSRafael J. Wysocki 
1260596ba34bSRafael J. Wysocki /**
1261596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
1262596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1263596ba34bSRafael J. Wysocki  *
1264596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1265596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1266596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1267596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1268596ba34bSRafael J. Wysocki  */
1269596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
1270596ba34bSRafael J. Wysocki {
1271596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1272596ba34bSRafael J. Wysocki 	bool run_complete;
1273596ba34bSRafael J. Wysocki 
1274596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1275596ba34bSRafael J. Wysocki 
1276596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1277596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1278596ba34bSRafael J. Wysocki 		return;
1279596ba34bSRafael J. Wysocki 
1280596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
1281596ba34bSRafael J. Wysocki 
1282596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
1283596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
1284596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
1285596ba34bSRafael J. Wysocki 
1286596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
1287596ba34bSRafael J. Wysocki 
1288596ba34bSRafael J. Wysocki 	if (run_complete) {
1289596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
12906f00ff78SRafael J. Wysocki 		pm_runtime_set_active(dev);
1291596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
12926f00ff78SRafael J. Wysocki 		pm_runtime_idle(dev);
1293596ba34bSRafael J. Wysocki 	}
1294596ba34bSRafael J. Wysocki }
1295596ba34bSRafael J. Wysocki 
129677f827deSRafael J. Wysocki /**
129777f827deSRafael J. Wysocki  * pm_genpd_syscore_switch - Switch power during system core suspend or resume.
129877f827deSRafael J. Wysocki  * @dev: Device that normally is marked as "always on" to switch power for.
129977f827deSRafael J. Wysocki  *
130077f827deSRafael J. Wysocki  * This routine may only be called during the system core (syscore) suspend or
130177f827deSRafael J. Wysocki  * resume phase for devices whose "always on" flags are set.
130277f827deSRafael J. Wysocki  */
130377f827deSRafael J. Wysocki void pm_genpd_syscore_switch(struct device *dev, bool suspend)
130477f827deSRafael J. Wysocki {
130577f827deSRafael J. Wysocki 	struct generic_pm_domain *genpd;
130677f827deSRafael J. Wysocki 
130777f827deSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
130877f827deSRafael J. Wysocki 	if (!pm_genpd_present(genpd))
130977f827deSRafael J. Wysocki 		return;
131077f827deSRafael J. Wysocki 
131177f827deSRafael J. Wysocki 	if (suspend) {
131277f827deSRafael J. Wysocki 		genpd->suspended_count++;
131377f827deSRafael J. Wysocki 		pm_genpd_sync_poweroff(genpd);
131477f827deSRafael J. Wysocki 	} else {
131577f827deSRafael J. Wysocki 		pm_genpd_sync_poweron(genpd);
131677f827deSRafael J. Wysocki 		genpd->suspended_count--;
131777f827deSRafael J. Wysocki 	}
131877f827deSRafael J. Wysocki }
131977f827deSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch);
132077f827deSRafael J. Wysocki 
1321596ba34bSRafael J. Wysocki #else
1322596ba34bSRafael J. Wysocki 
1323596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
1324596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
13250496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late		NULL
1326596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
13270496c8aeSRafael J. Wysocki #define pm_genpd_resume_early		NULL
1328596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
1329596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
1330596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
13310496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late		NULL
1332596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
13330496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early		NULL
1334596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
1335596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
1336596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
1337596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
1338596ba34bSRafael J. Wysocki 
1339596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1340596ba34bSRafael J. Wysocki 
13411d5fcfecSRafael J. Wysocki static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev)
13421d5fcfecSRafael J. Wysocki {
13431d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
13441d5fcfecSRafael J. Wysocki 
13451d5fcfecSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
13461d5fcfecSRafael J. Wysocki 	if (!gpd_data)
13471d5fcfecSRafael J. Wysocki 		return NULL;
13481d5fcfecSRafael J. Wysocki 
13491d5fcfecSRafael J. Wysocki 	mutex_init(&gpd_data->lock);
13501d5fcfecSRafael J. Wysocki 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
13511d5fcfecSRafael J. Wysocki 	dev_pm_qos_add_notifier(dev, &gpd_data->nb);
13521d5fcfecSRafael J. Wysocki 	return gpd_data;
13531d5fcfecSRafael J. Wysocki }
13541d5fcfecSRafael J. Wysocki 
13551d5fcfecSRafael J. Wysocki static void __pm_genpd_free_dev_data(struct device *dev,
13561d5fcfecSRafael J. Wysocki 				     struct generic_pm_domain_data *gpd_data)
13571d5fcfecSRafael J. Wysocki {
13581d5fcfecSRafael J. Wysocki 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
13591d5fcfecSRafael J. Wysocki 	kfree(gpd_data);
13601d5fcfecSRafael J. Wysocki }
13611d5fcfecSRafael J. Wysocki 
1362f721889fSRafael J. Wysocki /**
1363b02c999aSRafael J. Wysocki  * __pm_genpd_add_device - Add a device to an I/O PM domain.
1364f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
1365f721889fSRafael J. Wysocki  * @dev: Device to be added.
1366b02c999aSRafael J. Wysocki  * @td: Set of PM QoS timing parameters to attach to the device.
1367f721889fSRafael J. Wysocki  */
1368b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1369b02c999aSRafael J. Wysocki 			  struct gpd_timing_data *td)
1370f721889fSRafael J. Wysocki {
13711d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
13724605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1373f721889fSRafael J. Wysocki 	int ret = 0;
1374f721889fSRafael J. Wysocki 
1375f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1376f721889fSRafael J. Wysocki 
1377f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1378f721889fSRafael J. Wysocki 		return -EINVAL;
1379f721889fSRafael J. Wysocki 
13801d5fcfecSRafael J. Wysocki 	gpd_data_new = __pm_genpd_alloc_dev_data(dev);
13811d5fcfecSRafael J. Wysocki 	if (!gpd_data_new)
13826ff7bb0dSRafael J. Wysocki 		return -ENOMEM;
13836ff7bb0dSRafael J. Wysocki 
138417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1385f721889fSRafael J. Wysocki 
1386596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1387596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1388596ba34bSRafael J. Wysocki 		goto out;
1389596ba34bSRafael J. Wysocki 	}
1390596ba34bSRafael J. Wysocki 
13914605ab65SRafael J. Wysocki 	list_for_each_entry(pdd, &genpd->dev_list, list_node)
13924605ab65SRafael J. Wysocki 		if (pdd->dev == dev) {
1393f721889fSRafael J. Wysocki 			ret = -EINVAL;
1394f721889fSRafael J. Wysocki 			goto out;
1395f721889fSRafael J. Wysocki 		}
1396f721889fSRafael J. Wysocki 
13971d5fcfecSRafael J. Wysocki 	ret = dev_pm_get_subsys_data(dev);
13981d5fcfecSRafael J. Wysocki 	if (ret)
13991d5fcfecSRafael J. Wysocki 		goto out;
14001d5fcfecSRafael J. Wysocki 
1401596ba34bSRafael J. Wysocki 	genpd->device_count++;
14026ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1403f721889fSRafael J. Wysocki 
14046ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
14051d5fcfecSRafael J. Wysocki 
14066ff7bb0dSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
14071d5fcfecSRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
14081d5fcfecSRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
14091d5fcfecSRafael J. Wysocki 	} else {
14101d5fcfecSRafael J. Wysocki 		gpd_data = gpd_data_new;
1411cd0ea672SRafael J. Wysocki 		dev->power.subsys_data->domain_data = &gpd_data->base;
14121d5fcfecSRafael J. Wysocki 	}
14131d5fcfecSRafael J. Wysocki 	gpd_data->refcount++;
1414b02c999aSRafael J. Wysocki 	if (td)
1415b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1416f721889fSRafael J. Wysocki 
14171d5fcfecSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
14181d5fcfecSRafael J. Wysocki 
14191d5fcfecSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
14201d5fcfecSRafael J. Wysocki 	gpd_data->base.dev = dev;
14211d5fcfecSRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
14221d5fcfecSRafael J. Wysocki 	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
14236ff7bb0dSRafael J. Wysocki 	gpd_data->td.constraint_changed = true;
14246ff7bb0dSRafael J. Wysocki 	gpd_data->td.effective_constraint_ns = -1;
14256ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
14266ff7bb0dSRafael J. Wysocki 
1427f721889fSRafael J. Wysocki  out:
142817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1429f721889fSRafael J. Wysocki 
14301d5fcfecSRafael J. Wysocki 	if (gpd_data != gpd_data_new)
14311d5fcfecSRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data_new);
14321d5fcfecSRafael J. Wysocki 
1433f721889fSRafael J. Wysocki 	return ret;
1434f721889fSRafael J. Wysocki }
1435f721889fSRafael J. Wysocki 
1436f721889fSRafael J. Wysocki /**
1437c8aa130bSThomas Abraham  * __pm_genpd_of_add_device - Add a device to an I/O PM domain.
1438c8aa130bSThomas Abraham  * @genpd_node: Device tree node pointer representing a PM domain to which the
1439c8aa130bSThomas Abraham  *   the device is added to.
1440c8aa130bSThomas Abraham  * @dev: Device to be added.
1441c8aa130bSThomas Abraham  * @td: Set of PM QoS timing parameters to attach to the device.
1442c8aa130bSThomas Abraham  */
1443c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
1444c8aa130bSThomas Abraham 			     struct gpd_timing_data *td)
1445c8aa130bSThomas Abraham {
1446c8aa130bSThomas Abraham 	struct generic_pm_domain *genpd = NULL, *gpd;
1447c8aa130bSThomas Abraham 
1448c8aa130bSThomas Abraham 	dev_dbg(dev, "%s()\n", __func__);
1449c8aa130bSThomas Abraham 
1450c8aa130bSThomas Abraham 	if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
1451c8aa130bSThomas Abraham 		return -EINVAL;
1452c8aa130bSThomas Abraham 
1453c8aa130bSThomas Abraham 	mutex_lock(&gpd_list_lock);
1454c8aa130bSThomas Abraham 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
1455c8aa130bSThomas Abraham 		if (gpd->of_node == genpd_node) {
1456c8aa130bSThomas Abraham 			genpd = gpd;
1457c8aa130bSThomas Abraham 			break;
1458c8aa130bSThomas Abraham 		}
1459c8aa130bSThomas Abraham 	}
1460c8aa130bSThomas Abraham 	mutex_unlock(&gpd_list_lock);
1461c8aa130bSThomas Abraham 
1462c8aa130bSThomas Abraham 	if (!genpd)
1463c8aa130bSThomas Abraham 		return -EINVAL;
1464c8aa130bSThomas Abraham 
1465c8aa130bSThomas Abraham 	return __pm_genpd_add_device(genpd, dev, td);
1466c8aa130bSThomas Abraham }
1467c8aa130bSThomas Abraham 
1468c8aa130bSThomas Abraham /**
1469f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
1470f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
1471f721889fSRafael J. Wysocki  * @dev: Device to be removed.
1472f721889fSRafael J. Wysocki  */
1473f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1474f721889fSRafael J. Wysocki 			   struct device *dev)
1475f721889fSRafael J. Wysocki {
14766ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
14774605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
14781d5fcfecSRafael J. Wysocki 	bool remove = false;
1479efa69025SRafael J. Wysocki 	int ret = 0;
1480f721889fSRafael J. Wysocki 
1481f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1482f721889fSRafael J. Wysocki 
1483efa69025SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
1484efa69025SRafael J. Wysocki 	    ||  IS_ERR_OR_NULL(dev->pm_domain)
1485efa69025SRafael J. Wysocki 	    ||  pd_to_genpd(dev->pm_domain) != genpd)
1486f721889fSRafael J. Wysocki 		return -EINVAL;
1487f721889fSRafael J. Wysocki 
148817b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1489f721889fSRafael J. Wysocki 
1490596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1491596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1492596ba34bSRafael J. Wysocki 		goto out;
1493596ba34bSRafael J. Wysocki 	}
1494596ba34bSRafael J. Wysocki 
14956ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
14966ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
14976ff7bb0dSRafael J. Wysocki 
14986ff7bb0dSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
14991d5fcfecSRafael J. Wysocki 
1500f721889fSRafael J. Wysocki 	dev->pm_domain = NULL;
1501efa69025SRafael J. Wysocki 	pdd = dev->power.subsys_data->domain_data;
1502efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
15031d5fcfecSRafael J. Wysocki 	gpd_data = to_gpd_data(pdd);
15041d5fcfecSRafael J. Wysocki 	if (--gpd_data->refcount == 0) {
1505efa69025SRafael J. Wysocki 		dev->power.subsys_data->domain_data = NULL;
15061d5fcfecSRafael J. Wysocki 		remove = true;
15071d5fcfecSRafael J. Wysocki 	}
15081d5fcfecSRafael J. Wysocki 
15096ff7bb0dSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
1510f721889fSRafael J. Wysocki 
15116ff7bb0dSRafael J. Wysocki 	mutex_lock(&gpd_data->lock);
15126ff7bb0dSRafael J. Wysocki 	pdd->dev = NULL;
15136ff7bb0dSRafael J. Wysocki 	mutex_unlock(&gpd_data->lock);
15146ff7bb0dSRafael J. Wysocki 
15156ff7bb0dSRafael J. Wysocki 	genpd_release_lock(genpd);
15166ff7bb0dSRafael J. Wysocki 
15176ff7bb0dSRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
15181d5fcfecSRafael J. Wysocki 	if (remove)
15191d5fcfecSRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data);
15201d5fcfecSRafael J. Wysocki 
15216ff7bb0dSRafael J. Wysocki 	return 0;
1522f721889fSRafael J. Wysocki 
1523596ba34bSRafael J. Wysocki  out:
152417b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1525f721889fSRafael J. Wysocki 
1526f721889fSRafael J. Wysocki 	return ret;
1527f721889fSRafael J. Wysocki }
1528f721889fSRafael J. Wysocki 
1529f721889fSRafael J. Wysocki /**
1530ca1d72f0SRafael J. Wysocki  * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
1531ca1d72f0SRafael J. Wysocki  * @dev: Device to set/unset the flag for.
1532ca1d72f0SRafael J. Wysocki  * @val: The new value of the device's "need restore" flag.
1533ca1d72f0SRafael J. Wysocki  */
1534ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val)
1535ca1d72f0SRafael J. Wysocki {
1536ca1d72f0SRafael J. Wysocki 	struct pm_subsys_data *psd;
1537ca1d72f0SRafael J. Wysocki 	unsigned long flags;
1538ca1d72f0SRafael J. Wysocki 
1539ca1d72f0SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
1540ca1d72f0SRafael J. Wysocki 
1541ca1d72f0SRafael J. Wysocki 	psd = dev_to_psd(dev);
1542ca1d72f0SRafael J. Wysocki 	if (psd && psd->domain_data)
1543ca1d72f0SRafael J. Wysocki 		to_gpd_data(psd->domain_data)->need_restore = val;
1544ca1d72f0SRafael J. Wysocki 
1545ca1d72f0SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
1546ca1d72f0SRafael J. Wysocki }
1547ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
1548ca1d72f0SRafael J. Wysocki 
1549ca1d72f0SRafael J. Wysocki /**
1550f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
1551f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
1552bc0403ffSRafael J. Wysocki  * @subdomain: Subdomain to be added.
1553f721889fSRafael J. Wysocki  */
1554f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1555bc0403ffSRafael J. Wysocki 			   struct generic_pm_domain *subdomain)
1556f721889fSRafael J. Wysocki {
15575063ce15SRafael J. Wysocki 	struct gpd_link *link;
1558f721889fSRafael J. Wysocki 	int ret = 0;
1559f721889fSRafael J. Wysocki 
1560bc0403ffSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1561f721889fSRafael J. Wysocki 		return -EINVAL;
1562f721889fSRafael J. Wysocki 
156317b75ecaSRafael J. Wysocki  start:
156417b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1565bc0403ffSRafael J. Wysocki 	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1566f721889fSRafael J. Wysocki 
1567bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF
1568bc0403ffSRafael J. Wysocki 	    && subdomain->status != GPD_STATE_ACTIVE) {
1569bc0403ffSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
157017b75ecaSRafael J. Wysocki 		genpd_release_lock(genpd);
157117b75ecaSRafael J. Wysocki 		goto start;
157217b75ecaSRafael J. Wysocki 	}
157317b75ecaSRafael J. Wysocki 
157417b75ecaSRafael J. Wysocki 	if (genpd->status == GPD_STATE_POWER_OFF
1575bc0403ffSRafael J. Wysocki 	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
1576f721889fSRafael J. Wysocki 		ret = -EINVAL;
1577f721889fSRafael J. Wysocki 		goto out;
1578f721889fSRafael J. Wysocki 	}
1579f721889fSRafael J. Wysocki 
15804fcac10dSHuang Ying 	list_for_each_entry(link, &genpd->master_links, master_node) {
1581bc0403ffSRafael J. Wysocki 		if (link->slave == subdomain && link->master == genpd) {
1582f721889fSRafael J. Wysocki 			ret = -EINVAL;
1583f721889fSRafael J. Wysocki 			goto out;
1584f721889fSRafael J. Wysocki 		}
1585f721889fSRafael J. Wysocki 	}
1586f721889fSRafael J. Wysocki 
15875063ce15SRafael J. Wysocki 	link = kzalloc(sizeof(*link), GFP_KERNEL);
15885063ce15SRafael J. Wysocki 	if (!link) {
15895063ce15SRafael J. Wysocki 		ret = -ENOMEM;
15905063ce15SRafael J. Wysocki 		goto out;
15915063ce15SRafael J. Wysocki 	}
15925063ce15SRafael J. Wysocki 	link->master = genpd;
15935063ce15SRafael J. Wysocki 	list_add_tail(&link->master_node, &genpd->master_links);
1594bc0403ffSRafael J. Wysocki 	link->slave = subdomain;
1595bc0403ffSRafael J. Wysocki 	list_add_tail(&link->slave_node, &subdomain->slave_links);
1596bc0403ffSRafael J. Wysocki 	if (subdomain->status != GPD_STATE_POWER_OFF)
1597c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1598f721889fSRafael J. Wysocki 
1599f721889fSRafael J. Wysocki  out:
1600bc0403ffSRafael J. Wysocki 	mutex_unlock(&subdomain->lock);
160117b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1602f721889fSRafael J. Wysocki 
1603f721889fSRafael J. Wysocki 	return ret;
1604f721889fSRafael J. Wysocki }
1605f721889fSRafael J. Wysocki 
1606f721889fSRafael J. Wysocki /**
1607f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1608f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
16095063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1610f721889fSRafael J. Wysocki  */
1611f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
16125063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1613f721889fSRafael J. Wysocki {
16145063ce15SRafael J. Wysocki 	struct gpd_link *link;
1615f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1616f721889fSRafael J. Wysocki 
16175063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1618f721889fSRafael J. Wysocki 		return -EINVAL;
1619f721889fSRafael J. Wysocki 
162017b75ecaSRafael J. Wysocki  start:
162117b75ecaSRafael J. Wysocki 	genpd_acquire_lock(genpd);
1622f721889fSRafael J. Wysocki 
16235063ce15SRafael J. Wysocki 	list_for_each_entry(link, &genpd->master_links, master_node) {
16245063ce15SRafael J. Wysocki 		if (link->slave != subdomain)
1625f721889fSRafael J. Wysocki 			continue;
1626f721889fSRafael J. Wysocki 
1627f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
1628f721889fSRafael J. Wysocki 
162917b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF
163017b75ecaSRafael J. Wysocki 		    && subdomain->status != GPD_STATE_ACTIVE) {
163117b75ecaSRafael J. Wysocki 			mutex_unlock(&subdomain->lock);
163217b75ecaSRafael J. Wysocki 			genpd_release_lock(genpd);
163317b75ecaSRafael J. Wysocki 			goto start;
163417b75ecaSRafael J. Wysocki 		}
163517b75ecaSRafael J. Wysocki 
16365063ce15SRafael J. Wysocki 		list_del(&link->master_node);
16375063ce15SRafael J. Wysocki 		list_del(&link->slave_node);
16385063ce15SRafael J. Wysocki 		kfree(link);
163917b75ecaSRafael J. Wysocki 		if (subdomain->status != GPD_STATE_POWER_OFF)
1640f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1641f721889fSRafael J. Wysocki 
1642f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
1643f721889fSRafael J. Wysocki 
1644f721889fSRafael J. Wysocki 		ret = 0;
1645f721889fSRafael J. Wysocki 		break;
1646f721889fSRafael J. Wysocki 	}
1647f721889fSRafael J. Wysocki 
164817b75ecaSRafael J. Wysocki 	genpd_release_lock(genpd);
1649f721889fSRafael J. Wysocki 
1650f721889fSRafael J. Wysocki 	return ret;
1651f721889fSRafael J. Wysocki }
1652f721889fSRafael J. Wysocki 
1653f721889fSRafael J. Wysocki /**
1654d5e4cbfeSRafael J. Wysocki  * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
1655d5e4cbfeSRafael J. Wysocki  * @dev: Device to add the callbacks to.
1656d5e4cbfeSRafael J. Wysocki  * @ops: Set of callbacks to add.
1657b02c999aSRafael J. Wysocki  * @td: Timing data to add to the device along with the callbacks (optional).
165862d44902SRafael J. Wysocki  *
165962d44902SRafael J. Wysocki  * Every call to this routine should be balanced with a call to
166062d44902SRafael J. Wysocki  * __pm_genpd_remove_callbacks() and they must not be nested.
1661d5e4cbfeSRafael J. Wysocki  */
1662b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
1663b02c999aSRafael J. Wysocki 			   struct gpd_timing_data *td)
1664d5e4cbfeSRafael J. Wysocki {
166562d44902SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
1666d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1667d5e4cbfeSRafael J. Wysocki 
166862d44902SRafael J. Wysocki 	if (!(dev && ops))
1669d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1670d5e4cbfeSRafael J. Wysocki 
167162d44902SRafael J. Wysocki 	gpd_data_new = __pm_genpd_alloc_dev_data(dev);
167262d44902SRafael J. Wysocki 	if (!gpd_data_new)
167362d44902SRafael J. Wysocki 		return -ENOMEM;
167462d44902SRafael J. Wysocki 
1675d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1676d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1677d5e4cbfeSRafael J. Wysocki 
167862d44902SRafael J. Wysocki 	ret = dev_pm_get_subsys_data(dev);
167962d44902SRafael J. Wysocki 	if (ret)
168062d44902SRafael J. Wysocki 		goto out;
1681d5e4cbfeSRafael J. Wysocki 
168262d44902SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
168362d44902SRafael J. Wysocki 
168462d44902SRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
168562d44902SRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
168662d44902SRafael J. Wysocki 	} else {
168762d44902SRafael J. Wysocki 		gpd_data = gpd_data_new;
168862d44902SRafael J. Wysocki 		dev->power.subsys_data->domain_data = &gpd_data->base;
168962d44902SRafael J. Wysocki 	}
169062d44902SRafael J. Wysocki 	gpd_data->refcount++;
1691d5e4cbfeSRafael J. Wysocki 	gpd_data->ops = *ops;
1692b02c999aSRafael J. Wysocki 	if (td)
1693b02c999aSRafael J. Wysocki 		gpd_data->td = *td;
1694d5e4cbfeSRafael J. Wysocki 
169562d44902SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
169662d44902SRafael J. Wysocki 
169762d44902SRafael J. Wysocki  out:
1698d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1699d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1700d5e4cbfeSRafael J. Wysocki 
170162d44902SRafael J. Wysocki 	if (gpd_data != gpd_data_new)
170262d44902SRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data_new);
170362d44902SRafael J. Wysocki 
1704d5e4cbfeSRafael J. Wysocki 	return ret;
1705d5e4cbfeSRafael J. Wysocki }
1706d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
1707d5e4cbfeSRafael J. Wysocki 
1708d5e4cbfeSRafael J. Wysocki /**
1709b02c999aSRafael J. Wysocki  * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
1710d5e4cbfeSRafael J. Wysocki  * @dev: Device to remove the callbacks from.
1711b02c999aSRafael J. Wysocki  * @clear_td: If set, clear the device's timing data too.
171262d44902SRafael J. Wysocki  *
171362d44902SRafael J. Wysocki  * This routine can only be called after pm_genpd_add_callbacks().
1714d5e4cbfeSRafael J. Wysocki  */
1715b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
1716d5e4cbfeSRafael J. Wysocki {
171762d44902SRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data = NULL;
171862d44902SRafael J. Wysocki 	bool remove = false;
1719d5e4cbfeSRafael J. Wysocki 	int ret = 0;
1720d5e4cbfeSRafael J. Wysocki 
1721d5e4cbfeSRafael J. Wysocki 	if (!(dev && dev->power.subsys_data))
1722d5e4cbfeSRafael J. Wysocki 		return -EINVAL;
1723d5e4cbfeSRafael J. Wysocki 
1724d5e4cbfeSRafael J. Wysocki 	pm_runtime_disable(dev);
1725d5e4cbfeSRafael J. Wysocki 	device_pm_lock();
1726d5e4cbfeSRafael J. Wysocki 
172762d44902SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
1728d5e4cbfeSRafael J. Wysocki 
172962d44902SRafael J. Wysocki 	if (dev->power.subsys_data->domain_data) {
173062d44902SRafael J. Wysocki 		gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1731db79e53dSSachin Kamat 		gpd_data->ops = (struct gpd_dev_ops){ NULL };
1732b02c999aSRafael J. Wysocki 		if (clear_td)
1733b02c999aSRafael J. Wysocki 			gpd_data->td = (struct gpd_timing_data){ 0 };
173462d44902SRafael J. Wysocki 
173562d44902SRafael J. Wysocki 		if (--gpd_data->refcount == 0) {
173662d44902SRafael J. Wysocki 			dev->power.subsys_data->domain_data = NULL;
173762d44902SRafael J. Wysocki 			remove = true;
173862d44902SRafael J. Wysocki 		}
1739d5e4cbfeSRafael J. Wysocki 	} else {
1740d5e4cbfeSRafael J. Wysocki 		ret = -EINVAL;
1741d5e4cbfeSRafael J. Wysocki 	}
1742d5e4cbfeSRafael J. Wysocki 
174362d44902SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
174462d44902SRafael J. Wysocki 
1745d5e4cbfeSRafael J. Wysocki 	device_pm_unlock();
1746d5e4cbfeSRafael J. Wysocki 	pm_runtime_enable(dev);
1747d5e4cbfeSRafael J. Wysocki 
174862d44902SRafael J. Wysocki 	if (ret)
1749d5e4cbfeSRafael J. Wysocki 		return ret;
175062d44902SRafael J. Wysocki 
175162d44902SRafael J. Wysocki 	dev_pm_put_subsys_data(dev);
175262d44902SRafael J. Wysocki 	if (remove)
175362d44902SRafael J. Wysocki 		__pm_genpd_free_dev_data(dev, gpd_data);
175462d44902SRafael J. Wysocki 
175562d44902SRafael J. Wysocki 	return 0;
1756d5e4cbfeSRafael J. Wysocki }
1757b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
1758d5e4cbfeSRafael J. Wysocki 
1759cbc9ef02SRafael J. Wysocki int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)
1760cbc9ef02SRafael J. Wysocki {
1761cbc9ef02SRafael J. Wysocki 	struct cpuidle_driver *cpuidle_drv;
1762cbc9ef02SRafael J. Wysocki 	struct gpd_cpu_data *cpu_data;
1763cbc9ef02SRafael J. Wysocki 	struct cpuidle_state *idle_state;
1764cbc9ef02SRafael J. Wysocki 	int ret = 0;
1765cbc9ef02SRafael J. Wysocki 
1766cbc9ef02SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || state < 0)
1767cbc9ef02SRafael J. Wysocki 		return -EINVAL;
1768cbc9ef02SRafael J. Wysocki 
1769cbc9ef02SRafael J. Wysocki 	genpd_acquire_lock(genpd);
1770cbc9ef02SRafael J. Wysocki 
1771cbc9ef02SRafael J. Wysocki 	if (genpd->cpu_data) {
1772cbc9ef02SRafael J. Wysocki 		ret = -EEXIST;
1773cbc9ef02SRafael J. Wysocki 		goto out;
1774cbc9ef02SRafael J. Wysocki 	}
1775cbc9ef02SRafael J. Wysocki 	cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL);
1776cbc9ef02SRafael J. Wysocki 	if (!cpu_data) {
1777cbc9ef02SRafael J. Wysocki 		ret = -ENOMEM;
1778cbc9ef02SRafael J. Wysocki 		goto out;
1779cbc9ef02SRafael J. Wysocki 	}
1780cbc9ef02SRafael J. Wysocki 	cpuidle_drv = cpuidle_driver_ref();
1781cbc9ef02SRafael J. Wysocki 	if (!cpuidle_drv) {
1782cbc9ef02SRafael J. Wysocki 		ret = -ENODEV;
1783cbc9ef02SRafael J. Wysocki 		goto out;
1784cbc9ef02SRafael J. Wysocki 	}
1785cbc9ef02SRafael J. Wysocki 	if (cpuidle_drv->state_count <= state) {
1786cbc9ef02SRafael J. Wysocki 		ret = -EINVAL;
1787cbc9ef02SRafael J. Wysocki 		goto err;
1788cbc9ef02SRafael J. Wysocki 	}
1789cbc9ef02SRafael J. Wysocki 	idle_state = &cpuidle_drv->states[state];
1790cbc9ef02SRafael J. Wysocki 	if (!idle_state->disabled) {
1791cbc9ef02SRafael J. Wysocki 		ret = -EAGAIN;
1792cbc9ef02SRafael J. Wysocki 		goto err;
1793cbc9ef02SRafael J. Wysocki 	}
1794cbc9ef02SRafael J. Wysocki 	cpu_data->idle_state = idle_state;
1795cbc9ef02SRafael J. Wysocki 	cpu_data->saved_exit_latency = idle_state->exit_latency;
1796cbc9ef02SRafael J. Wysocki 	genpd->cpu_data = cpu_data;
1797cbc9ef02SRafael J. Wysocki 	genpd_recalc_cpu_exit_latency(genpd);
1798cbc9ef02SRafael J. Wysocki 
1799cbc9ef02SRafael J. Wysocki  out:
1800cbc9ef02SRafael J. Wysocki 	genpd_release_lock(genpd);
1801cbc9ef02SRafael J. Wysocki 	return ret;
1802cbc9ef02SRafael J. Wysocki 
1803cbc9ef02SRafael J. Wysocki  err:
1804cbc9ef02SRafael J. Wysocki 	cpuidle_driver_unref();
1805cbc9ef02SRafael J. Wysocki 	goto out;
1806cbc9ef02SRafael J. Wysocki }
1807cbc9ef02SRafael J. Wysocki 
1808cbc9ef02SRafael J. Wysocki int genpd_detach_cpuidle(struct generic_pm_domain *genpd)
1809cbc9ef02SRafael J. Wysocki {
1810cbc9ef02SRafael J. Wysocki 	struct gpd_cpu_data *cpu_data;
1811cbc9ef02SRafael J. Wysocki 	struct cpuidle_state *idle_state;
1812cbc9ef02SRafael J. Wysocki 	int ret = 0;
1813cbc9ef02SRafael J. Wysocki 
1814cbc9ef02SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1815cbc9ef02SRafael J. Wysocki 		return -EINVAL;
1816cbc9ef02SRafael J. Wysocki 
1817cbc9ef02SRafael J. Wysocki 	genpd_acquire_lock(genpd);
1818cbc9ef02SRafael J. Wysocki 
1819cbc9ef02SRafael J. Wysocki 	cpu_data = genpd->cpu_data;
1820cbc9ef02SRafael J. Wysocki 	if (!cpu_data) {
1821cbc9ef02SRafael J. Wysocki 		ret = -ENODEV;
1822cbc9ef02SRafael J. Wysocki 		goto out;
1823cbc9ef02SRafael J. Wysocki 	}
1824cbc9ef02SRafael J. Wysocki 	idle_state = cpu_data->idle_state;
1825cbc9ef02SRafael J. Wysocki 	if (!idle_state->disabled) {
1826cbc9ef02SRafael J. Wysocki 		ret = -EAGAIN;
1827cbc9ef02SRafael J. Wysocki 		goto out;
1828cbc9ef02SRafael J. Wysocki 	}
1829cbc9ef02SRafael J. Wysocki 	idle_state->exit_latency = cpu_data->saved_exit_latency;
1830cbc9ef02SRafael J. Wysocki 	cpuidle_driver_unref();
1831cbc9ef02SRafael J. Wysocki 	genpd->cpu_data = NULL;
1832cbc9ef02SRafael J. Wysocki 	kfree(cpu_data);
1833cbc9ef02SRafael J. Wysocki 
1834cbc9ef02SRafael J. Wysocki  out:
1835cbc9ef02SRafael J. Wysocki 	genpd_release_lock(genpd);
1836cbc9ef02SRafael J. Wysocki 	return ret;
1837cbc9ef02SRafael J. Wysocki }
1838cbc9ef02SRafael J. Wysocki 
1839d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */
1840d23b9b00SRafael J. Wysocki 
1841d5e4cbfeSRafael J. Wysocki /**
1842ecf00475SRafael J. Wysocki  * pm_genpd_default_save_state - Default "save device state" for PM domians.
1843ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1844ecf00475SRafael J. Wysocki  */
1845ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev)
1846ecf00475SRafael J. Wysocki {
1847ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1848ecf00475SRafael J. Wysocki 
1849ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.save_state;
1850ecf00475SRafael J. Wysocki 	if (cb)
1851ecf00475SRafael J. Wysocki 		return cb(dev);
1852ecf00475SRafael J. Wysocki 
18530b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
18540b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_suspend;
18550b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
18560b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_suspend;
18570b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
18580b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_suspend;
18590b589741SRafael J. Wysocki 	else
18600b589741SRafael J. Wysocki 		cb = NULL;
1861ecf00475SRafael J. Wysocki 
18620b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
18630b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_suspend;
18640b589741SRafael J. Wysocki 
18650b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1866ecf00475SRafael J. Wysocki }
1867ecf00475SRafael J. Wysocki 
1868ecf00475SRafael J. Wysocki /**
1869ecf00475SRafael J. Wysocki  * pm_genpd_default_restore_state - Default PM domians "restore device state".
1870ecf00475SRafael J. Wysocki  * @dev: Device to handle.
1871ecf00475SRafael J. Wysocki  */
1872ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev)
1873ecf00475SRafael J. Wysocki {
1874ecf00475SRafael J. Wysocki 	int (*cb)(struct device *__dev);
1875ecf00475SRafael J. Wysocki 
1876ecf00475SRafael J. Wysocki 	cb = dev_gpd_data(dev)->ops.restore_state;
1877ecf00475SRafael J. Wysocki 	if (cb)
1878ecf00475SRafael J. Wysocki 		return cb(dev);
1879ecf00475SRafael J. Wysocki 
18800b589741SRafael J. Wysocki 	if (dev->type && dev->type->pm)
18810b589741SRafael J. Wysocki 		cb = dev->type->pm->runtime_resume;
18820b589741SRafael J. Wysocki 	else if (dev->class && dev->class->pm)
18830b589741SRafael J. Wysocki 		cb = dev->class->pm->runtime_resume;
18840b589741SRafael J. Wysocki 	else if (dev->bus && dev->bus->pm)
18850b589741SRafael J. Wysocki 		cb = dev->bus->pm->runtime_resume;
18860b589741SRafael J. Wysocki 	else
18870b589741SRafael J. Wysocki 		cb = NULL;
1888ecf00475SRafael J. Wysocki 
18890b589741SRafael J. Wysocki 	if (!cb && dev->driver && dev->driver->pm)
18900b589741SRafael J. Wysocki 		cb = dev->driver->pm->runtime_resume;
18910b589741SRafael J. Wysocki 
18920b589741SRafael J. Wysocki 	return cb ? cb(dev) : 0;
1893ecf00475SRafael J. Wysocki }
1894ecf00475SRafael J. Wysocki 
18950f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
18960f1d6986SRafael J. Wysocki 
1897ecf00475SRafael J. Wysocki /**
1898d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend - Default "device suspend" for PM domians.
1899d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1900d23b9b00SRafael J. Wysocki  */
1901d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev)
1902d23b9b00SRafael J. Wysocki {
1903c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
1904d23b9b00SRafael J. Wysocki 
1905d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend(dev);
1906d23b9b00SRafael J. Wysocki }
1907d23b9b00SRafael J. Wysocki 
1908d23b9b00SRafael J. Wysocki /**
1909d23b9b00SRafael J. Wysocki  * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
1910d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1911d23b9b00SRafael J. Wysocki  */
1912d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev)
1913d23b9b00SRafael J. Wysocki {
1914c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
1915d23b9b00SRafael J. Wysocki 
19160496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_suspend_late(dev);
1917d23b9b00SRafael J. Wysocki }
1918d23b9b00SRafael J. Wysocki 
1919d23b9b00SRafael J. Wysocki /**
1920d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
1921d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1922d23b9b00SRafael J. Wysocki  */
1923d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev)
1924d23b9b00SRafael J. Wysocki {
1925c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
1926d23b9b00SRafael J. Wysocki 
19270496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume_early(dev);
1928d23b9b00SRafael J. Wysocki }
1929d23b9b00SRafael J. Wysocki 
1930d23b9b00SRafael J. Wysocki /**
1931d23b9b00SRafael J. Wysocki  * pm_genpd_default_resume - Default "device resume" for PM domians.
1932d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1933d23b9b00SRafael J. Wysocki  */
1934d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev)
1935d23b9b00SRafael J. Wysocki {
1936c9914854SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
1937d23b9b00SRafael J. Wysocki 
1938d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_resume(dev);
1939d23b9b00SRafael J. Wysocki }
1940d23b9b00SRafael J. Wysocki 
1941d23b9b00SRafael J. Wysocki /**
1942d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze - Default "device freeze" for PM domians.
1943d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1944d23b9b00SRafael J. Wysocki  */
1945d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev)
1946d23b9b00SRafael J. Wysocki {
1947d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
1948d23b9b00SRafael J. Wysocki 
1949d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze(dev);
1950d23b9b00SRafael J. Wysocki }
1951d23b9b00SRafael J. Wysocki 
1952d23b9b00SRafael J. Wysocki /**
1953d23b9b00SRafael J. Wysocki  * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
1954d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1955d23b9b00SRafael J. Wysocki  */
1956d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev)
1957d23b9b00SRafael J. Wysocki {
1958d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
1959d23b9b00SRafael J. Wysocki 
19600496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_freeze_late(dev);
1961d23b9b00SRafael J. Wysocki }
1962d23b9b00SRafael J. Wysocki 
1963d23b9b00SRafael J. Wysocki /**
1964d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
1965d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1966d23b9b00SRafael J. Wysocki  */
1967d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev)
1968d23b9b00SRafael J. Wysocki {
1969d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
1970d23b9b00SRafael J. Wysocki 
19710496c8aeSRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw_early(dev);
1972d23b9b00SRafael J. Wysocki }
1973d23b9b00SRafael J. Wysocki 
1974d23b9b00SRafael J. Wysocki /**
1975d23b9b00SRafael J. Wysocki  * pm_genpd_default_thaw - Default "device thaw" for PM domians.
1976d23b9b00SRafael J. Wysocki  * @dev: Device to handle.
1977d23b9b00SRafael J. Wysocki  */
1978d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev)
1979d23b9b00SRafael J. Wysocki {
1980d23b9b00SRafael J. Wysocki 	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
1981d23b9b00SRafael J. Wysocki 
1982d23b9b00SRafael J. Wysocki 	return cb ? cb(dev) : pm_generic_thaw(dev);
1983d23b9b00SRafael J. Wysocki }
1984d23b9b00SRafael J. Wysocki 
19850f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
19860f1d6986SRafael J. Wysocki 
19870f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend	NULL
19880f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late	NULL
19890f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early	NULL
19900f1d6986SRafael J. Wysocki #define pm_genpd_default_resume		NULL
19910f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze		NULL
19920f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late	NULL
19930f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early	NULL
19940f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw		NULL
19950f1d6986SRafael J. Wysocki 
19960f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */
19970f1d6986SRafael J. Wysocki 
1998d23b9b00SRafael J. Wysocki /**
1999f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
2000f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
2001f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
2002f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
2003f721889fSRafael J. Wysocki  */
2004f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
2005f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
2006f721889fSRafael J. Wysocki {
2007f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
2008f721889fSRafael J. Wysocki 		return;
2009f721889fSRafael J. Wysocki 
20105063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->master_links);
20115063ce15SRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->slave_links);
2012f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
2013f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
2014f721889fSRafael J. Wysocki 	genpd->gov = gov;
2015f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
2016f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
2017c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
201817b75ecaSRafael J. Wysocki 	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
201917b75ecaSRafael J. Wysocki 	init_waitqueue_head(&genpd->status_wait_queue);
2020c6d22b37SRafael J. Wysocki 	genpd->poweroff_task = NULL;
2021c6d22b37SRafael J. Wysocki 	genpd->resume_count = 0;
2022596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
2023221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
20246ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
2025f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
2026f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
2027f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
2028596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
2029596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
20300496c8aeSRafael J. Wysocki 	genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
2031596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
2032596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
20330496c8aeSRafael J. Wysocki 	genpd->domain.ops.resume_early = pm_genpd_resume_early;
2034596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
2035596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
20360496c8aeSRafael J. Wysocki 	genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
2037596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
2038596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
20390496c8aeSRafael J. Wysocki 	genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
2040596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
2041d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_suspend;
20420496c8aeSRafael J. Wysocki 	genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
2043d23b9b00SRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
2044596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
20450496c8aeSRafael J. Wysocki 	genpd->domain.ops.restore_early = pm_genpd_resume_early;
2046d23b9b00SRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_resume;
2047596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
2048ecf00475SRafael J. Wysocki 	genpd->dev_ops.save_state = pm_genpd_default_save_state;
2049ecf00475SRafael J. Wysocki 	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
2050c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend = pm_genpd_default_suspend;
2051c9914854SRafael J. Wysocki 	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
2052c9914854SRafael J. Wysocki 	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
2053c9914854SRafael J. Wysocki 	genpd->dev_ops.resume = pm_genpd_default_resume;
2054d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze = pm_genpd_default_freeze;
2055d23b9b00SRafael J. Wysocki 	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
2056d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
2057d23b9b00SRafael J. Wysocki 	genpd->dev_ops.thaw = pm_genpd_default_thaw;
20585125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
20595125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
20605125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
20615125bbf3SRafael J. Wysocki }
2062