aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-07-01 16:12:45 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-07-02 08:29:55 -0400
commitf721889ff65afa6243c463832c74dee3bed418d5 (patch)
tree350bcb4fd7964167189fce93d0a9352f2958b1a4 /drivers/base
parentdc6e4e56e6ef473a696a1ab24f80b79b9aceb92d (diff)
PM / Domains: Support for generic I/O PM domains (v8)
Introduce common headers, helper functions and callbacks allowing platforms to use simple generic power domains for runtime power management. Introduce struct generic_pm_domain to be used for representing power domains that each contain a number of devices and may be parent domains or subdomains with respect to other power domains. Among other things, this structure includes callbacks to be provided by platforms for performing specific tasks related to power management (i.e. ->stop_device() may disable a device's clocks, while ->start_device() may enable them, ->power_off() is supposed to remove power from the entire power domain and ->power_on() is supposed to restore it). Introduce functions that can be used as power domain runtime PM callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(), as well as helper functions for the initialization of a power domain represented by a struct generic_power_domain object, adding a device to or removing a device from it and adding or removing subdomains. Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be selected by the platforms that want to use the new code. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Greg Kroah-Hartman <gregkh@suse.de> Reviewed-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/Makefile1
-rw-r--r--drivers/base/power/domain.c494
2 files changed, 495 insertions, 0 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 3647e114d0e7..2639ae79a372 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
3obj-$(CONFIG_PM_RUNTIME) += runtime.o 3obj-$(CONFIG_PM_RUNTIME) += runtime.o
4obj-$(CONFIG_PM_TRACE_RTC) += trace.o 4obj-$(CONFIG_PM_TRACE_RTC) += trace.o
5obj-$(CONFIG_PM_OPP) += opp.o 5obj-$(CONFIG_PM_OPP) += opp.o
6obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o
6obj-$(CONFIG_HAVE_CLK) += clock_ops.o 7obj-$(CONFIG_HAVE_CLK) += clock_ops.o
7 8
8ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG \ No newline at end of file 9ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG \ No newline at end of file
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
new file mode 100644
index 000000000000..fd31be3be404
--- /dev/null
+++ b/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
1/*
2 * drivers/base/power/domain.c - Common code related to device power domains.
3 *
4 * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
5 *
6 * This file is released under the GPLv2.
7 */
8
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/io.h>
12#include <linux/pm_runtime.h>
13#include <linux/pm_domain.h>
14#include <linux/slab.h>
15#include <linux/err.h>
16
17#ifdef CONFIG_PM_RUNTIME
18
19static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
20{
21 if (!WARN_ON(genpd->sd_count == 0))
22 genpd->sd_count--;
23}
24
25/**
26 * __pm_genpd_save_device - Save the pre-suspend state of a device.
27 * @dle: Device list entry of the device to save the state of.
28 * @genpd: PM domain the device belongs to.
29 */
30static int __pm_genpd_save_device(struct dev_list_entry *dle,
31 struct generic_pm_domain *genpd)
32{
33 struct device *dev = dle->dev;
34 struct device_driver *drv = dev->driver;
35 int ret = 0;
36
37 if (dle->need_restore)
38 return 0;
39
40 if (drv && drv->pm && drv->pm->runtime_suspend) {
41 if (genpd->start_device)
42 genpd->start_device(dev);
43
44 ret = drv->pm->runtime_suspend(dev);
45
46 if (genpd->stop_device)
47 genpd->stop_device(dev);
48 }
49
50 if (!ret)
51 dle->need_restore = true;
52
53 return ret;
54}
55
56/**
57 * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
58 * @dle: Device list entry of the device to restore the state of.
59 * @genpd: PM domain the device belongs to.
60 */
61static void __pm_genpd_restore_device(struct dev_list_entry *dle,
62 struct generic_pm_domain *genpd)
63{
64 struct device *dev = dle->dev;
65 struct device_driver *drv = dev->driver;
66
67 if (!dle->need_restore)
68 return;
69
70 if (drv && drv->pm && drv->pm->runtime_resume) {
71 if (genpd->start_device)
72 genpd->start_device(dev);
73
74 drv->pm->runtime_resume(dev);
75
76 if (genpd->stop_device)
77 genpd->stop_device(dev);
78 }
79
80 dle->need_restore = false;
81}
82
83/**
84 * pm_genpd_poweroff - Remove power from a given PM domain.
85 * @genpd: PM domain to power down.
86 *
87 * If all of the @genpd's devices have been suspended and all of its subdomains
88 * have been powered down, run the runtime suspend callbacks provided by all of
89 * the @genpd's devices' drivers and remove power from @genpd.
90 */
91static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
92{
93 struct generic_pm_domain *parent;
94 struct dev_list_entry *dle;
95 unsigned int not_suspended;
96 int ret;
97
98 if (genpd->power_is_off)
99 return 0;
100
101 if (genpd->sd_count > 0)
102 return -EBUSY;
103
104 not_suspended = 0;
105 list_for_each_entry(dle, &genpd->dev_list, node)
106 if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
107 not_suspended++;
108
109 if (not_suspended > genpd->in_progress)
110 return -EBUSY;
111
112 if (genpd->gov && genpd->gov->power_down_ok) {
113 if (!genpd->gov->power_down_ok(&genpd->domain))
114 return -EAGAIN;
115 }
116
117 list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
118 ret = __pm_genpd_save_device(dle, genpd);
119 if (ret)
120 goto err_dev;
121 }
122
123 if (genpd->power_off)
124 genpd->power_off(genpd);
125
126 genpd->power_is_off = true;
127
128 parent = genpd->parent;
129 if (parent) {
130 genpd_sd_counter_dec(parent);
131 if (parent->sd_count == 0)
132 queue_work(pm_wq, &parent->power_off_work);
133 }
134
135 return 0;
136
137 err_dev:
138 list_for_each_entry_continue(dle, &genpd->dev_list, node)
139 __pm_genpd_restore_device(dle, genpd);
140
141 return ret;
142}
143
144/**
145 * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
146 * @work: Work structure used for scheduling the execution of this function.
147 */
148static void genpd_power_off_work_fn(struct work_struct *work)
149{
150 struct generic_pm_domain *genpd;
151
152 genpd = container_of(work, struct generic_pm_domain, power_off_work);
153
154 if (genpd->parent)
155 mutex_lock(&genpd->parent->lock);
156 mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
157 pm_genpd_poweroff(genpd);
158 mutex_unlock(&genpd->lock);
159 if (genpd->parent)
160 mutex_unlock(&genpd->parent->lock);
161}
162
163/**
164 * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
165 * @dev: Device to suspend.
166 *
167 * Carry out a runtime suspend of a device under the assumption that its
168 * pm_domain field points to the domain member of an object of type
169 * struct generic_pm_domain representing a PM domain consisting of I/O devices.
170 */
171static int pm_genpd_runtime_suspend(struct device *dev)
172{
173 struct generic_pm_domain *genpd;
174
175 dev_dbg(dev, "%s()\n", __func__);
176
177 if (IS_ERR_OR_NULL(dev->pm_domain))
178 return -EINVAL;
179
180 genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
181
182 if (genpd->parent)
183 mutex_lock(&genpd->parent->lock);
184 mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
185
186 if (genpd->stop_device) {
187 int ret = genpd->stop_device(dev);
188 if (ret)
189 goto out;
190 }
191 genpd->in_progress++;
192 pm_genpd_poweroff(genpd);
193 genpd->in_progress--;
194
195 out:
196 mutex_unlock(&genpd->lock);
197 if (genpd->parent)
198 mutex_unlock(&genpd->parent->lock);
199
200 return 0;
201}
202
203/**
204 * pm_genpd_poweron - Restore power to a given PM domain and its parents.
205 * @genpd: PM domain to power up.
206 *
207 * Restore power to @genpd and all of its parents so that it is possible to
208 * resume a device belonging to it.
209 */
210static int pm_genpd_poweron(struct generic_pm_domain *genpd)
211{
212 int ret = 0;
213
214 start:
215 if (genpd->parent)
216 mutex_lock(&genpd->parent->lock);
217 mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
218
219 if (!genpd->power_is_off)
220 goto out;
221
222 if (genpd->parent && genpd->parent->power_is_off) {
223 mutex_unlock(&genpd->lock);
224 mutex_unlock(&genpd->parent->lock);
225
226 ret = pm_genpd_poweron(genpd->parent);
227 if (ret)
228 return ret;
229
230 goto start;
231 }
232
233 if (genpd->power_on) {
234 int ret = genpd->power_on(genpd);
235 if (ret)
236 goto out;
237 }
238
239 genpd->power_is_off = false;
240 if (genpd->parent)
241 genpd->parent->sd_count++;
242
243 out:
244 mutex_unlock(&genpd->lock);
245 if (genpd->parent)
246 mutex_unlock(&genpd->parent->lock);
247
248 return ret;
249}
250
251/**
252 * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
253 * @dev: Device to resume.
254 *
255 * Carry out a runtime resume of a device under the assumption that its
256 * pm_domain field points to the domain member of an object of type
257 * struct generic_pm_domain representing a PM domain consisting of I/O devices.
258 */
259static int pm_genpd_runtime_resume(struct device *dev)
260{
261 struct generic_pm_domain *genpd;
262 struct dev_list_entry *dle;
263 int ret;
264
265 dev_dbg(dev, "%s()\n", __func__);
266
267 if (IS_ERR_OR_NULL(dev->pm_domain))
268 return -EINVAL;
269
270 genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
271
272 ret = pm_genpd_poweron(genpd);
273 if (ret)
274 return ret;
275
276 mutex_lock(&genpd->lock);
277
278 list_for_each_entry(dle, &genpd->dev_list, node) {
279 if (dle->dev == dev) {
280 __pm_genpd_restore_device(dle, genpd);
281 break;
282 }
283 }
284
285 if (genpd->start_device)
286 genpd->start_device(dev);
287
288 mutex_unlock(&genpd->lock);
289
290 return 0;
291}
292
293#else
294
295static inline void genpd_power_off_work_fn(struct work_struct *work) {}
296
297#define pm_genpd_runtime_suspend NULL
298#define pm_genpd_runtime_resume NULL
299
300#endif /* CONFIG_PM_RUNTIME */
301
302/**
303 * pm_genpd_add_device - Add a device to an I/O PM domain.
304 * @genpd: PM domain to add the device to.
305 * @dev: Device to be added.
306 */
307int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
308{
309 struct dev_list_entry *dle;
310 int ret = 0;
311
312 dev_dbg(dev, "%s()\n", __func__);
313
314 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
315 return -EINVAL;
316
317 mutex_lock(&genpd->lock);
318
319 if (genpd->power_is_off) {
320 ret = -EINVAL;
321 goto out;
322 }
323
324 list_for_each_entry(dle, &genpd->dev_list, node)
325 if (dle->dev == dev) {
326 ret = -EINVAL;
327 goto out;
328 }
329
330 dle = kzalloc(sizeof(*dle), GFP_KERNEL);
331 if (!dle) {
332 ret = -ENOMEM;
333 goto out;
334 }
335
336 dle->dev = dev;
337 dle->need_restore = false;
338 list_add_tail(&dle->node, &genpd->dev_list);
339
340 spin_lock_irq(&dev->power.lock);
341 dev->pm_domain = &genpd->domain;
342 spin_unlock_irq(&dev->power.lock);
343
344 out:
345 mutex_unlock(&genpd->lock);
346
347 return ret;
348}
349
350/**
351 * pm_genpd_remove_device - Remove a device from an I/O PM domain.
352 * @genpd: PM domain to remove the device from.
353 * @dev: Device to be removed.
354 */
355int pm_genpd_remove_device(struct generic_pm_domain *genpd,
356 struct device *dev)
357{
358 struct dev_list_entry *dle;
359 int ret = -EINVAL;
360
361 dev_dbg(dev, "%s()\n", __func__);
362
363 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
364 return -EINVAL;
365
366 mutex_lock(&genpd->lock);
367
368 list_for_each_entry(dle, &genpd->dev_list, node) {
369 if (dle->dev != dev)
370 continue;
371
372 spin_lock_irq(&dev->power.lock);
373 dev->pm_domain = NULL;
374 spin_unlock_irq(&dev->power.lock);
375
376 list_del(&dle->node);
377 kfree(dle);
378
379 ret = 0;
380 break;
381 }
382
383 mutex_unlock(&genpd->lock);
384
385 return ret;
386}
387
388/**
389 * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
390 * @genpd: Master PM domain to add the subdomain to.
391 * @new_subdomain: Subdomain to be added.
392 */
393int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
394 struct generic_pm_domain *new_subdomain)
395{
396 struct generic_pm_domain *subdomain;
397 int ret = 0;
398
399 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
400 return -EINVAL;
401
402 mutex_lock(&genpd->lock);
403
404 if (genpd->power_is_off && !new_subdomain->power_is_off) {
405 ret = -EINVAL;
406 goto out;
407 }
408
409 list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
410 if (subdomain == new_subdomain) {
411 ret = -EINVAL;
412 goto out;
413 }
414 }
415
416 mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
417
418 list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
419 new_subdomain->parent = genpd;
420 if (!subdomain->power_is_off)
421 genpd->sd_count++;
422
423 mutex_unlock(&new_subdomain->lock);
424
425 out:
426 mutex_unlock(&genpd->lock);
427
428 return ret;
429}
430
431/**
432 * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
433 * @genpd: Master PM domain to remove the subdomain from.
434 * @target: Subdomain to be removed.
435 */
436int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
437 struct generic_pm_domain *target)
438{
439 struct generic_pm_domain *subdomain;
440 int ret = -EINVAL;
441
442 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
443 return -EINVAL;
444
445 mutex_lock(&genpd->lock);
446
447 list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
448 if (subdomain != target)
449 continue;
450
451 mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
452
453 list_del(&subdomain->sd_node);
454 subdomain->parent = NULL;
455 if (!subdomain->power_is_off)
456 genpd_sd_counter_dec(genpd);
457
458 mutex_unlock(&subdomain->lock);
459
460 ret = 0;
461 break;
462 }
463
464 mutex_unlock(&genpd->lock);
465
466 return ret;
467}
468
469/**
470 * pm_genpd_init - Initialize a generic I/O PM domain object.
471 * @genpd: PM domain object to initialize.
472 * @gov: PM domain governor to associate with the domain (may be NULL).
473 * @is_off: Initial value of the domain's power_is_off field.
474 */
475void pm_genpd_init(struct generic_pm_domain *genpd,
476 struct dev_power_governor *gov, bool is_off)
477{
478 if (IS_ERR_OR_NULL(genpd))
479 return;
480
481 INIT_LIST_HEAD(&genpd->sd_node);
482 genpd->parent = NULL;
483 INIT_LIST_HEAD(&genpd->dev_list);
484 INIT_LIST_HEAD(&genpd->sd_list);
485 mutex_init(&genpd->lock);
486 genpd->gov = gov;
487 INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
488 genpd->in_progress = 0;
489 genpd->sd_count = 0;
490 genpd->power_is_off = is_off;
491 genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
492 genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
493 genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
494}