Blame view

drivers/base/power/domain.c 40.4 KB
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * drivers/base/power/domain.c - Common code related to device power domains.
   *
   * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
   *
   * This file is released under the GPLv2.
   */
  
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/io.h>
  #include <linux/pm_runtime.h>
  #include <linux/pm_domain.h>
  #include <linux/slab.h>
  #include <linux/err.h>
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
16
17
  #include <linux/sched.h>
  #include <linux/suspend.h>
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  #include <linux/export.h>
  
  #define GENPD_DEV_CALLBACK(genpd, type, callback, dev)		\
  ({								\
  	type (*__routine)(struct device *__d); 			\
  	type __ret = (type)0;					\
  								\
  	__routine = genpd->dev_ops.callback; 			\
  	if (__routine) {					\
  		__ret = __routine(dev); 			\
  	} else {						\
  		__routine = dev_gpd_data(dev)->ops.callback;	\
  		if (__routine) 					\
  			__ret = __routine(dev);			\
  	}							\
  	__ret;							\
  })
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
35

0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  #define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name)	\
  ({										\
  	ktime_t __start = ktime_get();						\
  	type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);		\
  	s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));		\
  	struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev);		\
  	if (__elapsed > __gpd_data->td.field) {					\
  		__gpd_data->td.field = __elapsed;				\
  		dev_warn(dev, name " latency exceeded, new value %lld ns
  ",	\
  			__elapsed);						\
  	}									\
  	__retval;								\
  })
5125bbf38   Rafael J. Wysocki   PM / Domains: Int...
50
51
  static LIST_HEAD(gpd_list);
  static DEFINE_MUTEX(gpd_list_lock);
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
52
  #ifdef CONFIG_PM
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
53
  struct generic_pm_domain *dev_to_genpd(struct device *dev)
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
54
55
56
  {
  	if (IS_ERR_OR_NULL(dev->pm_domain))
  		return ERR_PTR(-EINVAL);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
57
  	return pd_to_genpd(dev->pm_domain);
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
58
  }
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
59

d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
60
61
  static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
62
63
  	return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev,
  					stop_latency_ns, "stop");
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
64
65
66
67
  }
  
  static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
68
69
  	return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev,
  					start_latency_ns, "start");
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
70
  }
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
71
72
  static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
73
74
  	return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev,
  					save_state_latency_ns, "state save");
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
75
76
77
78
  }
  
  static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
79
80
81
  	return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev,
  					restore_state_latency_ns,
  					"state restore");
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
82
  }
c4bb3160c   Rafael J. Wysocki   PM / Domains: Imp...
83
  static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
84
  {
c4bb3160c   Rafael J. Wysocki   PM / Domains: Imp...
85
86
87
88
89
90
91
92
93
94
95
96
  	bool ret = false;
  
  	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
  		ret = !!atomic_dec_and_test(&genpd->sd_count);
  
  	return ret;
  }
  
  static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
  {
  	atomic_inc(&genpd->sd_count);
  	smp_mb__after_atomic_inc();
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
97
  }
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
98
99
100
101
102
103
104
105
106
107
108
109
  static void genpd_acquire_lock(struct generic_pm_domain *genpd)
  {
  	DEFINE_WAIT(wait);
  
  	mutex_lock(&genpd->lock);
  	/*
  	 * Wait for the domain to transition into either the active,
  	 * or the power off state.
  	 */
  	for (;;) {
  		prepare_to_wait(&genpd->status_wait_queue, &wait,
  				TASK_UNINTERRUPTIBLE);
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
110
111
  		if (genpd->status == GPD_STATE_ACTIVE
  		    || genpd->status == GPD_STATE_POWER_OFF)
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  			break;
  		mutex_unlock(&genpd->lock);
  
  		schedule();
  
  		mutex_lock(&genpd->lock);
  	}
  	finish_wait(&genpd->status_wait_queue, &wait);
  }
  
  static void genpd_release_lock(struct generic_pm_domain *genpd)
  {
  	mutex_unlock(&genpd->lock);
  }
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
126
127
128
129
130
  static void genpd_set_active(struct generic_pm_domain *genpd)
  {
  	if (genpd->resume_count == 0)
  		genpd->status = GPD_STATE_ACTIVE;
  }
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
131
  /**
5063ce157   Rafael J. Wysocki   PM / Domains: All...
132
   * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
133
134
   * @genpd: PM domain to power up.
   *
5063ce157   Rafael J. Wysocki   PM / Domains: All...
135
   * Restore power to @genpd and all of its masters so that it is possible to
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
136
137
   * resume a device belonging to it.
   */
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
138
139
  int __pm_genpd_poweron(struct generic_pm_domain *genpd)
  	__releases(&genpd->lock) __acquires(&genpd->lock)
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
140
  {
5063ce157   Rafael J. Wysocki   PM / Domains: All...
141
  	struct gpd_link *link;
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
142
  	DEFINE_WAIT(wait);
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
143
  	int ret = 0;
5063ce157   Rafael J. Wysocki   PM / Domains: All...
144
  	/* If the domain's master is being waited for, we have to wait too. */
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
145
146
147
  	for (;;) {
  		prepare_to_wait(&genpd->status_wait_queue, &wait,
  				TASK_UNINTERRUPTIBLE);
17877eb5a   Rafael J. Wysocki   PM / Domains: Ren...
148
  		if (genpd->status != GPD_STATE_WAIT_MASTER)
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
149
150
  			break;
  		mutex_unlock(&genpd->lock);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
151

3f241775c   Rafael J. Wysocki   PM / Domains: Add...
152
153
154
155
156
  		schedule();
  
  		mutex_lock(&genpd->lock);
  	}
  	finish_wait(&genpd->status_wait_queue, &wait);
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
157

17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
158
  	if (genpd->status == GPD_STATE_ACTIVE
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
159
  	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
160
  		return 0;
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
161

c6d22b372   Rafael J. Wysocki   PM / Domains: All...
162
163
  	if (genpd->status != GPD_STATE_POWER_OFF) {
  		genpd_set_active(genpd);
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
164
  		return 0;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
165
  	}
5063ce157   Rafael J. Wysocki   PM / Domains: All...
166
167
168
169
170
171
172
  	/*
  	 * The list is guaranteed not to change while the loop below is being
  	 * executed, unless one of the masters' .power_on() callbacks fiddles
  	 * with it.
  	 */
  	list_for_each_entry(link, &genpd->slave_links, slave_node) {
  		genpd_sd_counter_inc(link->master);
17877eb5a   Rafael J. Wysocki   PM / Domains: Ren...
173
  		genpd->status = GPD_STATE_WAIT_MASTER;
3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
174

5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
175
  		mutex_unlock(&genpd->lock);
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
176

5063ce157   Rafael J. Wysocki   PM / Domains: All...
177
  		ret = pm_genpd_poweron(link->master);
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
178
179
  
  		mutex_lock(&genpd->lock);
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
180
181
  		/*
  		 * The "wait for parent" status is guaranteed not to change
5063ce157   Rafael J. Wysocki   PM / Domains: All...
182
  		 * while the master is powering on.
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
183
184
185
  		 */
  		genpd->status = GPD_STATE_POWER_OFF;
  		wake_up_all(&genpd->status_wait_queue);
5063ce157   Rafael J. Wysocki   PM / Domains: All...
186
187
  		if (ret) {
  			genpd_sd_counter_dec(link->master);
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
188
  			goto err;
5063ce157   Rafael J. Wysocki   PM / Domains: All...
189
  		}
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
190
  	}
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
191
  	if (genpd->power_on) {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
192
193
  		ktime_t time_start = ktime_get();
  		s64 elapsed_ns;
fe202fde5   Rafael J. Wysocki   PM / Domains: Fix...
194
  		ret = genpd->power_on(genpd);
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
195
196
  		if (ret)
  			goto err;
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
197
198
  
  		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
199
  		if (elapsed_ns > genpd->power_on_latency_ns) {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
200
  			genpd->power_on_latency_ns = elapsed_ns;
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
201
202
203
204
205
206
  			if (genpd->name)
  				pr_warning("%s: Power-on latency exceeded, "
  					"new value %lld ns
  ", genpd->name,
  					elapsed_ns);
  		}
3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
207
  	}
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
208

9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
209
  	genpd_set_active(genpd);
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
210
  	return 0;
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
211
212
  
   err:
5063ce157   Rafael J. Wysocki   PM / Domains: All...
213
214
  	list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
  		genpd_sd_counter_dec(link->master);
9e08cf429   Rafael J. Wysocki   PM / Domains: Mak...
215

3f241775c   Rafael J. Wysocki   PM / Domains: Add...
216
217
218
219
  	return ret;
  }
  
  /**
5063ce157   Rafael J. Wysocki   PM / Domains: All...
220
   * pm_genpd_poweron - Restore power to a given PM domain and its masters.
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
221
222
223
224
225
226
227
228
229
230
   * @genpd: PM domain to power up.
   */
  int pm_genpd_poweron(struct generic_pm_domain *genpd)
  {
  	int ret;
  
  	mutex_lock(&genpd->lock);
  	ret = __pm_genpd_poweron(genpd);
  	mutex_unlock(&genpd->lock);
  	return ret;
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
231
232
233
234
235
236
237
  }
  
  #endif /* CONFIG_PM */
  
  #ifdef CONFIG_PM_RUNTIME
  
  /**
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
238
   * __pm_genpd_save_device - Save the pre-suspend state of a device.
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
239
   * @pdd: Domain data of the device to save the state of.
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
240
241
   * @genpd: PM domain the device belongs to.
   */
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
242
  static int __pm_genpd_save_device(struct pm_domain_data *pdd,
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
243
  				  struct generic_pm_domain *genpd)
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
244
  	__releases(&genpd->lock) __acquires(&genpd->lock)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
245
  {
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
246
  	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
247
  	struct device *dev = pdd->dev;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
248
  	int ret = 0;
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
249
  	if (gpd_data->need_restore)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
250
  		return 0;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
251
  	mutex_unlock(&genpd->lock);
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
252
253
254
  	genpd_start_dev(genpd, dev);
  	ret = genpd_save_dev(genpd, dev);
  	genpd_stop_dev(genpd, dev);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
255

17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
256
  	mutex_lock(&genpd->lock);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
257
  	if (!ret)
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
258
  		gpd_data->need_restore = true;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
259
260
261
262
263
264
  
  	return ret;
  }
  
  /**
   * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
265
   * @pdd: Domain data of the device to restore the state of.
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
266
267
   * @genpd: PM domain the device belongs to.
   */
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
268
  static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
269
  				      struct generic_pm_domain *genpd)
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
270
  	__releases(&genpd->lock) __acquires(&genpd->lock)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
271
  {
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
272
  	struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
273
  	struct device *dev = pdd->dev;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
274

cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
275
  	if (!gpd_data->need_restore)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
276
  		return;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
277
  	mutex_unlock(&genpd->lock);
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
278
279
280
  	genpd_start_dev(genpd, dev);
  	genpd_restore_dev(genpd, dev);
  	genpd_stop_dev(genpd, dev);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
281

17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
282
  	mutex_lock(&genpd->lock);
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
283
  	gpd_data->need_restore = false;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
284
285
286
  }
  
  /**
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
287
288
289
290
291
292
293
294
295
296
   * genpd_abort_poweroff - Check if a PM domain power off should be aborted.
   * @genpd: PM domain to check.
   *
   * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
   * a "power off" operation, which means that a "power on" has occured in the
   * meantime, or if its resume_count field is different from zero, which means
   * that one of its devices has been resumed in the meantime.
   */
  static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
  {
17877eb5a   Rafael J. Wysocki   PM / Domains: Ren...
297
  	return genpd->status == GPD_STATE_WAIT_MASTER
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
298
  		|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
299
300
301
  }
  
  /**
56375fd42   Rafael J. Wysocki   PM / Domains: Que...
302
303
304
305
306
307
   * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
   * @genpd: PM domait to power off.
   *
   * Queue up the execution of pm_genpd_poweroff() unless it's already been done
   * before.
   */
0bc5b2deb   Rafael J. Wysocki   ARM / shmobile: U...
308
  void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
56375fd42   Rafael J. Wysocki   PM / Domains: Que...
309
310
311
312
313
314
  {
  	if (!work_pending(&genpd->power_off_work))
  		queue_work(pm_wq, &genpd->power_off_work);
  }
  
  /**
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
315
316
317
318
319
320
321
322
   * pm_genpd_poweroff - Remove power from a given PM domain.
   * @genpd: PM domain to power down.
   *
   * If all of the @genpd's devices have been suspended and all of its subdomains
   * have been powered down, run the runtime suspend callbacks provided by all of
   * the @genpd's devices' drivers and remove power from @genpd.
   */
  static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
323
  	__releases(&genpd->lock) __acquires(&genpd->lock)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
324
  {
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
325
  	struct pm_domain_data *pdd;
5063ce157   Rafael J. Wysocki   PM / Domains: All...
326
  	struct gpd_link *link;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
327
  	unsigned int not_suspended;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
328
  	int ret = 0;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
329

c6d22b372   Rafael J. Wysocki   PM / Domains: All...
330
331
332
333
   start:
  	/*
  	 * Do not try to power off the domain in the following situations:
  	 * (1) The domain is already in the "power off" state.
5063ce157   Rafael J. Wysocki   PM / Domains: All...
334
  	 * (2) The domain is waiting for its master to power up.
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
335
  	 * (3) One of the domain's devices is being resumed right now.
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
336
  	 * (4) System suspend is in progress.
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
337
  	 */
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
338
  	if (genpd->status == GPD_STATE_POWER_OFF
17877eb5a   Rafael J. Wysocki   PM / Domains: Ren...
339
  	    || genpd->status == GPD_STATE_WAIT_MASTER
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
340
  	    || genpd->resume_count > 0 || genpd->prepared_count > 0)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
341
  		return 0;
c4bb3160c   Rafael J. Wysocki   PM / Domains: Imp...
342
  	if (atomic_read(&genpd->sd_count) > 0)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
343
344
345
  		return -EBUSY;
  
  	not_suspended = 0;
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
346
  	list_for_each_entry(pdd, &genpd->dev_list, list_node)
0aa2a2216   Rafael J. Wysocki   PM / Domains: Pre...
347
348
  		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
  		    || pdd->dev->power.irq_safe))
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
349
350
351
352
  			not_suspended++;
  
  	if (not_suspended > genpd->in_progress)
  		return -EBUSY;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
353
354
355
356
357
358
359
360
  	if (genpd->poweroff_task) {
  		/*
  		 * Another instance of pm_genpd_poweroff() is executing
  		 * callbacks, so tell it to start over and return.
  		 */
  		genpd->status = GPD_STATE_REPEAT;
  		return 0;
  	}
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
361
362
363
364
  	if (genpd->gov && genpd->gov->power_down_ok) {
  		if (!genpd->gov->power_down_ok(&genpd->domain))
  			return -EAGAIN;
  	}
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
365
  	genpd->status = GPD_STATE_BUSY;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
366
  	genpd->poweroff_task = current;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
367

4605ab653   Rafael J. Wysocki   PM / Domains: Use...
368
  	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
369
  		ret = atomic_read(&genpd->sd_count) == 0 ?
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
370
  			__pm_genpd_save_device(pdd, genpd) : -EBUSY;
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
371
372
373
  
  		if (genpd_abort_poweroff(genpd))
  			goto out;
697a7f372   Rafael J. Wysocki   PM / Domains: Do ...
374
375
376
377
  		if (ret) {
  			genpd_set_active(genpd);
  			goto out;
  		}
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
378

c6d22b372   Rafael J. Wysocki   PM / Domains: All...
379
380
381
382
383
  		if (genpd->status == GPD_STATE_REPEAT) {
  			genpd->poweroff_task = NULL;
  			goto start;
  		}
  	}
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
384

3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
385
  	if (genpd->power_off) {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
386
387
  		ktime_t time_start;
  		s64 elapsed_ns;
3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
388
389
  		if (atomic_read(&genpd->sd_count) > 0) {
  			ret = -EBUSY;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
390
391
  			goto out;
  		}
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
392

0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
393
  		time_start = ktime_get();
3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
394
  		/*
5063ce157   Rafael J. Wysocki   PM / Domains: All...
395
396
  		 * If sd_count > 0 at this point, one of the subdomains hasn't
  		 * managed to call pm_genpd_poweron() for the master yet after
3c07cbc48   Rafael J. Wysocki   PM / Domains: Do ...
397
398
399
400
401
  		 * incrementing it.  In that case pm_genpd_poweron() will wait
  		 * for us to drop the lock, so we can call .power_off() and let
  		 * the pm_genpd_poweron() restore power for us (this shouldn't
  		 * happen very often).
  		 */
d28054020   Rafael J. Wysocki   PM / Domains: Tak...
402
403
404
  		ret = genpd->power_off(genpd);
  		if (ret == -EBUSY) {
  			genpd_set_active(genpd);
d28054020   Rafael J. Wysocki   PM / Domains: Tak...
405
406
  			goto out;
  		}
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
407
408
  
  		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
409
  		if (elapsed_ns > genpd->power_off_latency_ns) {
0140d8bd4   Rafael J. Wysocki   PM / Domains: Aut...
410
  			genpd->power_off_latency_ns = elapsed_ns;
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
411
412
413
414
415
416
  			if (genpd->name)
  				pr_warning("%s: Power-off latency exceeded, "
  					"new value %lld ns
  ", genpd->name,
  					elapsed_ns);
  		}
d28054020   Rafael J. Wysocki   PM / Domains: Tak...
417
  	}
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
418

17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
419
  	genpd->status = GPD_STATE_POWER_OFF;
221e9b583   Rafael J. Wysocki   PM / Domains: Add...
420
421
422
423
424
425
426
427
428
429
430
  	genpd->power_off_time = ktime_get();
  
  	/* Update PM QoS information for devices in the domain. */
  	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
  		struct gpd_timing_data *td = &to_gpd_data(pdd)->td;
  
  		pm_runtime_update_max_time_suspended(pdd->dev,
  					td->start_latency_ns +
  					td->restore_state_latency_ns +
  					genpd->power_on_latency_ns);
  	}
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
431

5063ce157   Rafael J. Wysocki   PM / Domains: All...
432
433
434
435
  	list_for_each_entry(link, &genpd->slave_links, slave_node) {
  		genpd_sd_counter_dec(link->master);
  		genpd_queue_power_off_work(link->master);
  	}
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
436

c6d22b372   Rafael J. Wysocki   PM / Domains: All...
437
438
439
440
   out:
  	genpd->poweroff_task = NULL;
  	wake_up_all(&genpd->status_wait_queue);
  	return ret;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
441
442
443
444
445
446
447
448
449
450
451
  }
  
  /**
   * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
   * @work: Work structure used for scheduling the execution of this function.
   */
  static void genpd_power_off_work_fn(struct work_struct *work)
  {
  	struct generic_pm_domain *genpd;
  
  	genpd = container_of(work, struct generic_pm_domain, power_off_work);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
452
  	genpd_acquire_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
453
  	pm_genpd_poweroff(genpd);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
454
  	genpd_release_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
455
456
457
458
459
460
461
462
463
464
465
466
467
  }
  
  /**
   * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
   * @dev: Device to suspend.
   *
   * Carry out a runtime suspend of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a PM domain consisting of I/O devices.
   */
  static int pm_genpd_runtime_suspend(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
468
  	bool (*stop_ok)(struct device *__dev);
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
469
  	int ret;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
470
471
472
  
  	dev_dbg(dev, "%s()
  ", __func__);
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
473
474
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
475
  		return -EINVAL;
0aa2a2216   Rafael J. Wysocki   PM / Domains: Pre...
476
  	might_sleep_if(!genpd->dev_irq_safe);
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
477
478
479
  	stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
  	if (stop_ok && !stop_ok(dev))
  		return -EBUSY;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
480
481
482
  	ret = genpd_stop_dev(genpd, dev);
  	if (ret)
  		return ret;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
483

b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
484
485
  	pm_runtime_update_max_time_suspended(dev,
  				dev_gpd_data(dev)->td.start_latency_ns);
0aa2a2216   Rafael J. Wysocki   PM / Domains: Pre...
486
487
488
489
490
491
  	/*
  	 * If power.irq_safe is set, this routine will be run with interrupts
  	 * off, so it can't use mutexes.
  	 */
  	if (dev->power.irq_safe)
  		return 0;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
492
  	mutex_lock(&genpd->lock);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
493
494
495
  	genpd->in_progress++;
  	pm_genpd_poweroff(genpd);
  	genpd->in_progress--;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
496
  	mutex_unlock(&genpd->lock);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
497
498
499
500
501
  
  	return 0;
  }
  
  /**
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
502
503
504
505
506
507
508
509
510
511
   * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
   * @dev: Device to resume.
   *
   * Carry out a runtime resume of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a PM domain consisting of I/O devices.
   */
  static int pm_genpd_runtime_resume(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
512
  	DEFINE_WAIT(wait);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
513
514
515
516
  	int ret;
  
  	dev_dbg(dev, "%s()
  ", __func__);
5248051b9   Rafael J. Wysocki   PM / Domains: Mov...
517
518
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
519
  		return -EINVAL;
0aa2a2216   Rafael J. Wysocki   PM / Domains: Pre...
520
521
522
523
524
  	might_sleep_if(!genpd->dev_irq_safe);
  
  	/* If power.irq_safe, the PM domain is never powered off. */
  	if (dev->power.irq_safe)
  		goto out;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
525
  	mutex_lock(&genpd->lock);
3f241775c   Rafael J. Wysocki   PM / Domains: Add...
526
527
528
529
530
  	ret = __pm_genpd_poweron(genpd);
  	if (ret) {
  		mutex_unlock(&genpd->lock);
  		return ret;
  	}
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
531
  	genpd->status = GPD_STATE_BUSY;
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
  	genpd->resume_count++;
  	for (;;) {
  		prepare_to_wait(&genpd->status_wait_queue, &wait,
  				TASK_UNINTERRUPTIBLE);
  		/*
  		 * If current is the powering off task, we have been called
  		 * reentrantly from one of the device callbacks, so we should
  		 * not wait.
  		 */
  		if (!genpd->poweroff_task || genpd->poweroff_task == current)
  			break;
  		mutex_unlock(&genpd->lock);
  
  		schedule();
  
  		mutex_lock(&genpd->lock);
  	}
  	finish_wait(&genpd->status_wait_queue, &wait);
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
550
  	__pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd);
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
551
552
  	genpd->resume_count--;
  	genpd_set_active(genpd);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
553
  	wake_up_all(&genpd->status_wait_queue);
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
554
  	mutex_unlock(&genpd->lock);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
555

0aa2a2216   Rafael J. Wysocki   PM / Domains: Pre...
556
   out:
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
557
  	genpd_start_dev(genpd, dev);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
558
559
560
  
  	return 0;
  }
17f2ae7f6   Rafael J. Wysocki   PM / Domains: Fix...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
  /**
   * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
   */
  void pm_genpd_poweroff_unused(void)
  {
  	struct generic_pm_domain *genpd;
  
  	mutex_lock(&gpd_list_lock);
  
  	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
  		genpd_queue_power_off_work(genpd);
  
  	mutex_unlock(&gpd_list_lock);
  }
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
575
576
577
578
579
580
581
582
  #else
  
  static inline void genpd_power_off_work_fn(struct work_struct *work) {}
  
  #define pm_genpd_runtime_suspend	NULL
  #define pm_genpd_runtime_resume		NULL
  
  #endif /* CONFIG_PM_RUNTIME */
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
583
  #ifdef CONFIG_PM_SLEEP
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
584
585
586
587
588
  static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
  				    struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
  }
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, suspend, dev);
  }
  
  static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev);
  }
  
  static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev);
  }
  
  static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, resume, dev);
  }
  
  static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, freeze, dev);
  }
  
  static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev);
  }
  
  static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev);
  }
  
  static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
  {
  	return GENPD_DEV_CALLBACK(genpd, int, thaw, dev);
  }
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
628
  /**
5063ce157   Rafael J. Wysocki   PM / Domains: All...
629
   * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
630
631
632
   * @genpd: PM domain to power off, if possible.
   *
   * Check if the given PM domain can be powered off (during system suspend or
5063ce157   Rafael J. Wysocki   PM / Domains: All...
633
   * hibernation) and do that if so.  Also, in that case propagate to its masters.
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
634
635
636
637
638
639
640
   *
   * This function is only called in "noirq" stages of system power transitions,
   * so it need not acquire locks (all of the "noirq" callbacks are executed
   * sequentially, so it is guaranteed that it will never run twice in parallel).
   */
  static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
  {
5063ce157   Rafael J. Wysocki   PM / Domains: All...
641
  	struct gpd_link *link;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
642

17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
643
  	if (genpd->status == GPD_STATE_POWER_OFF)
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
644
  		return;
c4bb3160c   Rafael J. Wysocki   PM / Domains: Imp...
645
646
  	if (genpd->suspended_count != genpd->device_count
  	    || atomic_read(&genpd->sd_count) > 0)
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
647
648
649
650
  		return;
  
  	if (genpd->power_off)
  		genpd->power_off(genpd);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
651
  	genpd->status = GPD_STATE_POWER_OFF;
5063ce157   Rafael J. Wysocki   PM / Domains: All...
652
653
654
655
  
  	list_for_each_entry(link, &genpd->slave_links, slave_node) {
  		genpd_sd_counter_dec(link->master);
  		pm_genpd_sync_poweroff(link->master);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
656
657
658
659
  	}
  }
  
  /**
4ecd6e651   Rafael J. Wysocki   PM / Domains: Imp...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
   * resume_needed - Check whether to resume a device before system suspend.
   * @dev: Device to check.
   * @genpd: PM domain the device belongs to.
   *
   * There are two cases in which a device that can wake up the system from sleep
   * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled
   * to wake up the system and it has to remain active for this purpose while the
   * system is in the sleep state and (2) if the device is not enabled to wake up
   * the system from sleep states and it generally doesn't generate wakeup signals
   * by itself (those signals are generated on its behalf by other parts of the
   * system).  In the latter case it may be necessary to reconfigure the device's
   * wakeup settings during system suspend, because it may have been set up to
   * signal remote wakeup from the system's working state as needed by runtime PM.
   * Return 'true' in either of the above cases.
   */
  static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
  {
  	bool active_wakeup;
  
  	if (!device_can_wakeup(dev))
  		return false;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
681
  	active_wakeup = genpd_dev_active_wakeup(genpd, dev);
4ecd6e651   Rafael J. Wysocki   PM / Domains: Imp...
682
683
684
685
  	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
  }
  
  /**
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
686
687
688
689
690
691
692
693
694
695
696
   * pm_genpd_prepare - Start power transition of a device in a PM domain.
   * @dev: Device to start the transition of.
   *
   * Start a power transition of a device (during a system-wide power transition)
   * under the assumption that its pm_domain field points to the domain member of
   * an object of type struct generic_pm_domain representing a PM domain
   * consisting of I/O devices.
   */
  static int pm_genpd_prepare(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
b6c10c846   Rafael J. Wysocki   PM / Domains: Mak...
697
  	int ret;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
698
699
700
701
702
703
704
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
705
706
707
708
709
710
711
712
713
714
715
716
717
  	/*
  	 * If a wakeup request is pending for the device, it should be woken up
  	 * at this point and a system wakeup event should be reported if it's
  	 * set up to wake up the system from sleep states.
  	 */
  	pm_runtime_get_noresume(dev);
  	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
  		pm_wakeup_event(dev, 0);
  
  	if (pm_wakeup_pending()) {
  		pm_runtime_put_sync(dev);
  		return -EBUSY;
  	}
4ecd6e651   Rafael J. Wysocki   PM / Domains: Imp...
718
719
  	if (resume_needed(dev, genpd))
  		pm_runtime_resume(dev);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
720
  	genpd_acquire_lock(genpd);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
721
722
  
  	if (genpd->prepared_count++ == 0)
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
723
724
725
  		genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
  
  	genpd_release_lock(genpd);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
726
727
  
  	if (genpd->suspend_power_off) {
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
728
  		pm_runtime_put_noidle(dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
729
730
731
732
  		return 0;
  	}
  
  	/*
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
733
734
  	 * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
  	 * so pm_genpd_poweron() will return immediately, but if the device
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
735
  	 * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
736
  	 * to make it operational.
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
737
  	 */
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
738
  	pm_runtime_resume(dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
739
  	__pm_runtime_disable(dev, false);
b6c10c846   Rafael J. Wysocki   PM / Domains: Mak...
740
741
742
743
744
745
746
747
  	ret = pm_generic_prepare(dev);
  	if (ret) {
  		mutex_lock(&genpd->lock);
  
  		if (--genpd->prepared_count == 0)
  			genpd->suspend_power_off = false;
  
  		mutex_unlock(&genpd->lock);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
748
  		pm_runtime_enable(dev);
b6c10c846   Rafael J. Wysocki   PM / Domains: Mak...
749
  	}
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
750
751
  
  	pm_runtime_put_sync(dev);
b6c10c846   Rafael J. Wysocki   PM / Domains: Mak...
752
  	return ret;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
  }
  
  /**
   * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
   * @dev: Device to suspend.
   *
   * Suspend a device under the assumption that its pm_domain field points to the
   * domain member of an object of type struct generic_pm_domain representing
   * a PM domain consisting of I/O devices.
   */
  static int pm_genpd_suspend(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
773
  	return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
  }
  
  /**
   * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
   * @dev: Device to suspend.
   *
   * Carry out a late suspend of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a PM domain consisting of I/O devices.
   */
  static int pm_genpd_suspend_noirq(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  	int ret;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
  
  	if (genpd->suspend_power_off)
  		return 0;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
798
  	ret = genpd_suspend_late(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
799
800
  	if (ret)
  		return ret;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
801
  	if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
d4f2d87a8   Rafael J. Wysocki   PM / Domains: Wak...
802
  		return 0;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
803
  	genpd_stop_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
  
  	/*
  	 * Since all of the "noirq" callbacks are executed sequentially, it is
  	 * guaranteed that this function will never run twice in parallel for
  	 * the same PM domain, so it is not necessary to use locking here.
  	 */
  	genpd->suspended_count++;
  	pm_genpd_sync_poweroff(genpd);
  
  	return 0;
  }
  
  /**
   * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
   * @dev: Device to resume.
   *
   * Carry out an early resume of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a power domain consisting of I/O
   * devices.
   */
  static int pm_genpd_resume_noirq(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
  
  	if (genpd->suspend_power_off)
  		return 0;
  
  	/*
  	 * Since all of the "noirq" callbacks are executed sequentially, it is
  	 * guaranteed that this function will never run twice in parallel for
  	 * the same PM domain, so it is not necessary to use locking here.
  	 */
  	pm_genpd_poweron(genpd);
  	genpd->suspended_count--;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
846
  	genpd_start_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
847

d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
848
  	return genpd_resume_early(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
  }
  
  /**
   * pm_genpd_resume - Resume a device belonging to an I/O power domain.
   * @dev: Device to resume.
   *
   * Resume a device under the assumption that its pm_domain field points to the
   * domain member of an object of type struct generic_pm_domain representing
   * a power domain consisting of I/O devices.
   */
  static int pm_genpd_resume(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
869
  	return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
  }
  
  /**
   * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
   * @dev: Device to freeze.
   *
   * Freeze a device under the assumption that its pm_domain field points to the
   * domain member of an object of type struct generic_pm_domain representing
   * a power domain consisting of I/O devices.
   */
  static int pm_genpd_freeze(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
890
  	return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
  }
  
  /**
   * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
   * @dev: Device to freeze.
   *
   * Carry out a late freeze of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a power domain consisting of I/O
   * devices.
   */
  static int pm_genpd_freeze_noirq(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  	int ret;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
  
  	if (genpd->suspend_power_off)
  		return 0;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
916
  	ret = genpd_freeze_late(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
917
918
  	if (ret)
  		return ret;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
919
  	genpd_stop_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
  
  	return 0;
  }
  
  /**
   * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
   * @dev: Device to thaw.
   *
   * Carry out an early thaw of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a power domain consisting of I/O
   * devices.
   */
  static int pm_genpd_thaw_noirq(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
  
  	if (genpd->suspend_power_off)
  		return 0;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
946
  	genpd_start_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
947

d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
948
  	return genpd_thaw_early(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
  }
  
  /**
   * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
   * @dev: Device to thaw.
   *
   * Thaw a device under the assumption that its pm_domain field points to the
   * domain member of an object of type struct generic_pm_domain representing
   * a power domain consisting of I/O devices.
   */
  static int pm_genpd_thaw(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
969
  	return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
  }
  
  /**
   * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
   * @dev: Device to resume.
   *
   * Carry out an early restore of a device under the assumption that its
   * pm_domain field points to the domain member of an object of type
   * struct generic_pm_domain representing a power domain consisting of I/O
   * devices.
   */
  static int pm_genpd_restore_noirq(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return -EINVAL;
  
  	/*
  	 * Since all of the "noirq" callbacks are executed sequentially, it is
  	 * guaranteed that this function will never run twice in parallel for
  	 * the same PM domain, so it is not necessary to use locking here.
  	 */
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
997
  	genpd->status = GPD_STATE_POWER_OFF;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
  	if (genpd->suspend_power_off) {
  		/*
  		 * The boot kernel might put the domain into the power on state,
  		 * so make sure it really is powered off.
  		 */
  		if (genpd->power_off)
  			genpd->power_off(genpd);
  		return 0;
  	}
  
  	pm_genpd_poweron(genpd);
  	genpd->suspended_count--;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1010
  	genpd_start_dev(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1011

d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1012
  	return genpd_resume_early(genpd, dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
  }
  
  /**
   * pm_genpd_complete - Complete power transition of a device in a power domain.
   * @dev: Device to complete the transition of.
   *
   * Complete a power transition of a device (during a system-wide power
   * transition) under the assumption that its pm_domain field points to the
   * domain member of an object of type struct generic_pm_domain representing
   * a power domain consisting of I/O devices.
   */
  static void pm_genpd_complete(struct device *dev)
  {
  	struct generic_pm_domain *genpd;
  	bool run_complete;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	genpd = dev_to_genpd(dev);
  	if (IS_ERR(genpd))
  		return;
  
  	mutex_lock(&genpd->lock);
  
  	run_complete = !genpd->suspend_power_off;
  	if (--genpd->prepared_count == 0)
  		genpd->suspend_power_off = false;
  
  	mutex_unlock(&genpd->lock);
  
  	if (run_complete) {
  		pm_generic_complete(dev);
6f00ff782   Rafael J. Wysocki   PM / Domains: Set...
1046
  		pm_runtime_set_active(dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1047
  		pm_runtime_enable(dev);
6f00ff782   Rafael J. Wysocki   PM / Domains: Set...
1048
  		pm_runtime_idle(dev);
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
  	}
  }
  
  #else
  
  #define pm_genpd_prepare		NULL
  #define pm_genpd_suspend		NULL
  #define pm_genpd_suspend_noirq		NULL
  #define pm_genpd_resume_noirq		NULL
  #define pm_genpd_resume			NULL
  #define pm_genpd_freeze			NULL
  #define pm_genpd_freeze_noirq		NULL
  #define pm_genpd_thaw_noirq		NULL
  #define pm_genpd_thaw			NULL
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1063
  #define pm_genpd_restore_noirq		NULL
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1064
1065
1066
  #define pm_genpd_complete		NULL
  
  #endif /* CONFIG_PM_SLEEP */
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1067
  /**
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1068
   * __pm_genpd_add_device - Add a device to an I/O PM domain.
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1069
1070
   * @genpd: PM domain to add the device to.
   * @dev: Device to be added.
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1071
   * @td: Set of PM QoS timing parameters to attach to the device.
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1072
   */
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1073
1074
  int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
  			  struct gpd_timing_data *td)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1075
  {
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
1076
  	struct generic_pm_domain_data *gpd_data;
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
1077
  	struct pm_domain_data *pdd;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1078
1079
1080
1081
1082
1083
1084
  	int ret = 0;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
  		return -EINVAL;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1085
  	genpd_acquire_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1086

17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1087
  	if (genpd->status == GPD_STATE_POWER_OFF) {
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1088
1089
1090
  		ret = -EINVAL;
  		goto out;
  	}
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1091
1092
1093
1094
  	if (genpd->prepared_count > 0) {
  		ret = -EAGAIN;
  		goto out;
  	}
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
1095
1096
  	list_for_each_entry(pdd, &genpd->dev_list, list_node)
  		if (pdd->dev == dev) {
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1097
1098
1099
  			ret = -EINVAL;
  			goto out;
  		}
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
1100
1101
1102
1103
1104
  	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
  	if (!gpd_data) {
  		ret = -ENOMEM;
  		goto out;
  	}
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1105
  	genpd->device_count++;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1106

f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1107
  	dev->pm_domain = &genpd->domain;
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
1108
  	dev_pm_get_subsys_data(dev);
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
1109
1110
1111
1112
  	dev->power.subsys_data->domain_data = &gpd_data->base;
  	gpd_data->base.dev = dev;
  	gpd_data->need_restore = false;
  	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1113
1114
  	if (td)
  		gpd_data->td = *td;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1115
1116
  
   out:
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1117
  	genpd_release_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
  
  	return ret;
  }
  
  /**
   * pm_genpd_remove_device - Remove a device from an I/O PM domain.
   * @genpd: PM domain to remove the device from.
   * @dev: Device to be removed.
   */
  int pm_genpd_remove_device(struct generic_pm_domain *genpd,
  			   struct device *dev)
  {
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
1130
  	struct pm_domain_data *pdd;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1131
1132
1133
1134
1135
1136
1137
  	int ret = -EINVAL;
  
  	dev_dbg(dev, "%s()
  ", __func__);
  
  	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
  		return -EINVAL;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1138
  	genpd_acquire_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1139

596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1140
1141
1142
1143
  	if (genpd->prepared_count > 0) {
  		ret = -EAGAIN;
  		goto out;
  	}
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
1144
1145
  	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
  		if (pdd->dev != dev)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1146
  			continue;
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
1147
1148
1149
  		list_del_init(&pdd->list_node);
  		pdd->dev = NULL;
  		dev_pm_put_subsys_data(dev);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1150
  		dev->pm_domain = NULL;
cd0ea672f   Rafael J. Wysocki   PM / Domains: Spl...
1151
  		kfree(to_gpd_data(pdd));
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1152

596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1153
  		genpd->device_count--;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1154
1155
1156
1157
  
  		ret = 0;
  		break;
  	}
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1158
   out:
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1159
  	genpd_release_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1160
1161
1162
1163
1164
1165
1166
  
  	return ret;
  }
  
  /**
   * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
   * @genpd: Master PM domain to add the subdomain to.
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1167
   * @subdomain: Subdomain to be added.
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1168
1169
   */
  int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1170
  			   struct generic_pm_domain *subdomain)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1171
  {
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1172
  	struct gpd_link *link;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1173
  	int ret = 0;
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1174
  	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1175
  		return -EINVAL;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1176
1177
   start:
  	genpd_acquire_lock(genpd);
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1178
  	mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1179

bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1180
1181
1182
  	if (subdomain->status != GPD_STATE_POWER_OFF
  	    && subdomain->status != GPD_STATE_ACTIVE) {
  		mutex_unlock(&subdomain->lock);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1183
1184
1185
1186
1187
  		genpd_release_lock(genpd);
  		goto start;
  	}
  
  	if (genpd->status == GPD_STATE_POWER_OFF
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1188
  	    &&  subdomain->status != GPD_STATE_POWER_OFF) {
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1189
1190
1191
  		ret = -EINVAL;
  		goto out;
  	}
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1192
  	list_for_each_entry(link, &genpd->slave_links, slave_node) {
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1193
  		if (link->slave == subdomain && link->master == genpd) {
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1194
1195
1196
1197
  			ret = -EINVAL;
  			goto out;
  		}
  	}
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1198
1199
1200
1201
1202
1203
1204
  	link = kzalloc(sizeof(*link), GFP_KERNEL);
  	if (!link) {
  		ret = -ENOMEM;
  		goto out;
  	}
  	link->master = genpd;
  	list_add_tail(&link->master_node, &genpd->master_links);
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1205
1206
1207
  	link->slave = subdomain;
  	list_add_tail(&link->slave_node, &subdomain->slave_links);
  	if (subdomain->status != GPD_STATE_POWER_OFF)
c4bb3160c   Rafael J. Wysocki   PM / Domains: Imp...
1208
  		genpd_sd_counter_inc(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1209

f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1210
   out:
bc0403ff1   Rafael J. Wysocki   PM / Domains: Ren...
1211
  	mutex_unlock(&subdomain->lock);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1212
  	genpd_release_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1213
1214
1215
1216
1217
1218
1219
  
  	return ret;
  }
  
  /**
   * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
   * @genpd: Master PM domain to remove the subdomain from.
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1220
   * @subdomain: Subdomain to be removed.
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1221
1222
   */
  int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1223
  			      struct generic_pm_domain *subdomain)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1224
  {
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1225
  	struct gpd_link *link;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1226
  	int ret = -EINVAL;
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1227
  	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1228
  		return -EINVAL;
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1229
1230
   start:
  	genpd_acquire_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1231

5063ce157   Rafael J. Wysocki   PM / Domains: All...
1232
1233
  	list_for_each_entry(link, &genpd->master_links, master_node) {
  		if (link->slave != subdomain)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1234
1235
1236
  			continue;
  
  		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1237
1238
1239
1240
1241
1242
  		if (subdomain->status != GPD_STATE_POWER_OFF
  		    && subdomain->status != GPD_STATE_ACTIVE) {
  			mutex_unlock(&subdomain->lock);
  			genpd_release_lock(genpd);
  			goto start;
  		}
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1243
1244
1245
  		list_del(&link->master_node);
  		list_del(&link->slave_node);
  		kfree(link);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1246
  		if (subdomain->status != GPD_STATE_POWER_OFF)
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1247
1248
1249
1250
1251
1252
1253
  			genpd_sd_counter_dec(genpd);
  
  		mutex_unlock(&subdomain->lock);
  
  		ret = 0;
  		break;
  	}
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1254
  	genpd_release_lock(genpd);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1255
1256
1257
1258
1259
  
  	return ret;
  }
  
  /**
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1260
1261
1262
   * pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
   * @dev: Device to add the callbacks to.
   * @ops: Set of callbacks to add.
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1263
   * @td: Timing data to add to the device along with the callbacks (optional).
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1264
   */
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1265
1266
  int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops,
  			   struct gpd_timing_data *td)
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
  {
  	struct pm_domain_data *pdd;
  	int ret = 0;
  
  	if (!(dev && dev->power.subsys_data && ops))
  		return -EINVAL;
  
  	pm_runtime_disable(dev);
  	device_pm_lock();
  
  	pdd = dev->power.subsys_data->domain_data;
  	if (pdd) {
  		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
  
  		gpd_data->ops = *ops;
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1282
1283
  		if (td)
  			gpd_data->td = *td;
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
  	} else {
  		ret = -EINVAL;
  	}
  
  	device_pm_unlock();
  	pm_runtime_enable(dev);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
  
  /**
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1296
   * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1297
   * @dev: Device to remove the callbacks from.
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1298
   * @clear_td: If set, clear the device's timing data too.
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1299
   */
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1300
  int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
  {
  	struct pm_domain_data *pdd;
  	int ret = 0;
  
  	if (!(dev && dev->power.subsys_data))
  		return -EINVAL;
  
  	pm_runtime_disable(dev);
  	device_pm_lock();
  
  	pdd = dev->power.subsys_data->domain_data;
  	if (pdd) {
  		struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
  
  		gpd_data->ops = (struct gpd_dev_ops){ 0 };
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1316
1317
  		if (clear_td)
  			gpd_data->td = (struct gpd_timing_data){ 0 };
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1318
1319
1320
1321
1322
1323
1324
1325
1326
  	} else {
  		ret = -EINVAL;
  	}
  
  	device_pm_unlock();
  	pm_runtime_enable(dev);
  
  	return ret;
  }
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
1327
  EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks);
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1328

d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1329
  /* Default device callbacks for generic PM domains. */
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
1330
  /**
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
   * pm_genpd_default_save_state - Default "save device state" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_save_state(struct device *dev)
  {
  	int (*cb)(struct device *__dev);
  	struct device_driver *drv = dev->driver;
  
  	cb = dev_gpd_data(dev)->ops.save_state;
  	if (cb)
  		return cb(dev);
  
  	if (drv && drv->pm && drv->pm->runtime_suspend)
  		return drv->pm->runtime_suspend(dev);
  
  	return 0;
  }
  
  /**
   * pm_genpd_default_restore_state - Default PM domians "restore device state".
   * @dev: Device to handle.
   */
  static int pm_genpd_default_restore_state(struct device *dev)
  {
  	int (*cb)(struct device *__dev);
  	struct device_driver *drv = dev->driver;
  
  	cb = dev_gpd_data(dev)->ops.restore_state;
  	if (cb)
  		return cb(dev);
  
  	if (drv && drv->pm && drv->pm->runtime_resume)
  		return drv->pm->runtime_resume(dev);
  
  	return 0;
  }
0f1d6986b   Rafael J. Wysocki   PM / Domains: Fix...
1367
  #ifdef CONFIG_PM_SLEEP
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
1368
  /**
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1369
1370
1371
1372
1373
   * pm_genpd_default_suspend - Default "device suspend" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_suspend(struct device *dev)
  {
c9914854b   Rafael J. Wysocki   PM / Domains: Fix...
1374
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
  
  	return cb ? cb(dev) : pm_generic_suspend(dev);
  }
  
  /**
   * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_suspend_late(struct device *dev)
  {
c9914854b   Rafael J. Wysocki   PM / Domains: Fix...
1385
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
  
  	return cb ? cb(dev) : pm_generic_suspend_noirq(dev);
  }
  
  /**
   * pm_genpd_default_resume_early - Default "early device resume" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_resume_early(struct device *dev)
  {
c9914854b   Rafael J. Wysocki   PM / Domains: Fix...
1396
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
  
  	return cb ? cb(dev) : pm_generic_resume_noirq(dev);
  }
  
  /**
   * pm_genpd_default_resume - Default "device resume" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_resume(struct device *dev)
  {
c9914854b   Rafael J. Wysocki   PM / Domains: Fix...
1407
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
  
  	return cb ? cb(dev) : pm_generic_resume(dev);
  }
  
  /**
   * pm_genpd_default_freeze - Default "device freeze" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_freeze(struct device *dev)
  {
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze;
  
  	return cb ? cb(dev) : pm_generic_freeze(dev);
  }
  
  /**
   * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_freeze_late(struct device *dev)
  {
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
  
  	return cb ? cb(dev) : pm_generic_freeze_noirq(dev);
  }
  
  /**
   * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_thaw_early(struct device *dev)
  {
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
  
  	return cb ? cb(dev) : pm_generic_thaw_noirq(dev);
  }
  
  /**
   * pm_genpd_default_thaw - Default "device thaw" for PM domians.
   * @dev: Device to handle.
   */
  static int pm_genpd_default_thaw(struct device *dev)
  {
  	int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw;
  
  	return cb ? cb(dev) : pm_generic_thaw(dev);
  }
0f1d6986b   Rafael J. Wysocki   PM / Domains: Fix...
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
  #else /* !CONFIG_PM_SLEEP */
  
  #define pm_genpd_default_suspend	NULL
  #define pm_genpd_default_suspend_late	NULL
  #define pm_genpd_default_resume_early	NULL
  #define pm_genpd_default_resume		NULL
  #define pm_genpd_default_freeze		NULL
  #define pm_genpd_default_freeze_late	NULL
  #define pm_genpd_default_thaw_early	NULL
  #define pm_genpd_default_thaw		NULL
  
  #endif /* !CONFIG_PM_SLEEP */
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1467
  /**
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
   * pm_genpd_init - Initialize a generic I/O PM domain object.
   * @genpd: PM domain object to initialize.
   * @gov: PM domain governor to associate with the domain (may be NULL).
   * @is_off: Initial value of the domain's power_is_off field.
   */
  void pm_genpd_init(struct generic_pm_domain *genpd,
  		   struct dev_power_governor *gov, bool is_off)
  {
  	if (IS_ERR_OR_NULL(genpd))
  		return;
5063ce157   Rafael J. Wysocki   PM / Domains: All...
1478
1479
  	INIT_LIST_HEAD(&genpd->master_links);
  	INIT_LIST_HEAD(&genpd->slave_links);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1480
  	INIT_LIST_HEAD(&genpd->dev_list);
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1481
1482
1483
1484
  	mutex_init(&genpd->lock);
  	genpd->gov = gov;
  	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
  	genpd->in_progress = 0;
c4bb3160c   Rafael J. Wysocki   PM / Domains: Imp...
1485
  	atomic_set(&genpd->sd_count, 0);
17b75eca7   Rafael J. Wysocki   PM / Domains: Do ...
1486
1487
  	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
  	init_waitqueue_head(&genpd->status_wait_queue);
c6d22b372   Rafael J. Wysocki   PM / Domains: All...
1488
1489
  	genpd->poweroff_task = NULL;
  	genpd->resume_count = 0;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1490
1491
  	genpd->device_count = 0;
  	genpd->suspended_count = 0;
221e9b583   Rafael J. Wysocki   PM / Domains: Add...
1492
  	genpd->max_off_time_ns = -1;
f721889ff   Rafael J. Wysocki   PM / Domains: Sup...
1493
1494
1495
  	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
  	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
  	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1496
1497
1498
1499
1500
1501
1502
1503
1504
  	genpd->domain.ops.prepare = pm_genpd_prepare;
  	genpd->domain.ops.suspend = pm_genpd_suspend;
  	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
  	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
  	genpd->domain.ops.resume = pm_genpd_resume;
  	genpd->domain.ops.freeze = pm_genpd_freeze;
  	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
  	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
  	genpd->domain.ops.thaw = pm_genpd_thaw;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1505
1506
  	genpd->domain.ops.poweroff = pm_genpd_suspend;
  	genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1507
  	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1508
  	genpd->domain.ops.restore = pm_genpd_resume;
596ba34bc   Rafael J. Wysocki   PM / Domains: Sys...
1509
  	genpd->domain.ops.complete = pm_genpd_complete;
ecf00475f   Rafael J. Wysocki   PM / Domains: Int...
1510
1511
  	genpd->dev_ops.save_state = pm_genpd_default_save_state;
  	genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
c9914854b   Rafael J. Wysocki   PM / Domains: Fix...
1512
1513
1514
1515
  	genpd->dev_ops.suspend = pm_genpd_default_suspend;
  	genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late;
  	genpd->dev_ops.resume_early = pm_genpd_default_resume_early;
  	genpd->dev_ops.resume = pm_genpd_default_resume;
d23b9b00c   Rafael J. Wysocki   PM / Domains: Rew...
1516
1517
1518
1519
  	genpd->dev_ops.freeze = pm_genpd_default_freeze;
  	genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
  	genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
  	genpd->dev_ops.thaw = pm_genpd_default_thaw;
5125bbf38   Rafael J. Wysocki   PM / Domains: Int...
1520
1521
1522
1523
  	mutex_lock(&gpd_list_lock);
  	list_add(&genpd->gpd_list_node, &gpd_list);
  	mutex_unlock(&gpd_list_lock);
  }