2 * drivers/base/power/domain.c - Common code related to device power domains.
4 * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
6 * This file is released under the GPLv2.
9 #include <linux/init.h>
10 #include <linux/kernel.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/pm_domain.h>
14 #include <linux/slab.h>
15 #include <linux/err.h>
19 static struct generic_pm_domain *dev_to_genpd(struct device *dev)
21 if (IS_ERR_OR_NULL(dev->pm_domain))
22 return ERR_PTR(-EINVAL);
24 return container_of(dev->pm_domain, struct generic_pm_domain, domain);
27 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
29 if (!WARN_ON(genpd->sd_count == 0))
34 * pm_genpd_poweron - Restore power to a given PM domain and its parents.
35 * @genpd: PM domain to power up.
37 * Restore power to @genpd and all of its parents so that it is possible to
38 * resume a device belonging to it.
40 static int pm_genpd_poweron(struct generic_pm_domain *genpd)
46 mutex_lock(&genpd->parent->lock);
47 mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
49 if (!genpd->power_is_off)
52 if (genpd->parent && genpd->parent->power_is_off) {
53 mutex_unlock(&genpd->lock);
54 mutex_unlock(&genpd->parent->lock);
56 ret = pm_genpd_poweron(genpd->parent);
63 if (genpd->power_on) {
64 int ret = genpd->power_on(genpd);
69 genpd->power_is_off = false;
71 genpd->parent->sd_count++;
74 mutex_unlock(&genpd->lock);
76 mutex_unlock(&genpd->parent->lock);
81 #endif /* CONFIG_PM */
83 #ifdef CONFIG_PM_RUNTIME
86 * __pm_genpd_save_device - Save the pre-suspend state of a device.
87 * @dle: Device list entry of the device to save the state of.
88 * @genpd: PM domain the device belongs to.
90 static int __pm_genpd_save_device(struct dev_list_entry *dle,
91 struct generic_pm_domain *genpd)
93 struct device *dev = dle->dev;
94 struct device_driver *drv = dev->driver;
97 if (dle->need_restore)
100 if (drv && drv->pm && drv->pm->runtime_suspend) {
101 if (genpd->start_device)
102 genpd->start_device(dev);
104 ret = drv->pm->runtime_suspend(dev);
106 if (genpd->stop_device)
107 genpd->stop_device(dev);
111 dle->need_restore = true;
117 * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
118 * @dle: Device list entry of the device to restore the state of.
119 * @genpd: PM domain the device belongs to.
121 static void __pm_genpd_restore_device(struct dev_list_entry *dle,
122 struct generic_pm_domain *genpd)
124 struct device *dev = dle->dev;
125 struct device_driver *drv = dev->driver;
127 if (!dle->need_restore)
130 if (drv && drv->pm && drv->pm->runtime_resume) {
131 if (genpd->start_device)
132 genpd->start_device(dev);
134 drv->pm->runtime_resume(dev);
136 if (genpd->stop_device)
137 genpd->stop_device(dev);
140 dle->need_restore = false;
144 * pm_genpd_poweroff - Remove power from a given PM domain.
145 * @genpd: PM domain to power down.
147 * If all of the @genpd's devices have been suspended and all of its subdomains
148 * have been powered down, run the runtime suspend callbacks provided by all of
149 * the @genpd's devices' drivers and remove power from @genpd.
151 static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
153 struct generic_pm_domain *parent;
154 struct dev_list_entry *dle;
155 unsigned int not_suspended;
158 if (genpd->power_is_off)
161 if (genpd->sd_count > 0)
165 list_for_each_entry(dle, &genpd->dev_list, node)
166 if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
169 if (not_suspended > genpd->in_progress)
172 if (genpd->gov && genpd->gov->power_down_ok) {
173 if (!genpd->gov->power_down_ok(&genpd->domain))
177 list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
178 ret = __pm_genpd_save_device(dle, genpd);
183 if (genpd->power_off)
184 genpd->power_off(genpd);
186 genpd->power_is_off = true;
188 parent = genpd->parent;
190 genpd_sd_counter_dec(parent);
191 if (parent->sd_count == 0)
192 queue_work(pm_wq, &parent->power_off_work);
198 list_for_each_entry_continue(dle, &genpd->dev_list, node)
199 __pm_genpd_restore_device(dle, genpd);
205 * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
206 * @work: Work structure used for scheduling the execution of this function.
208 static void genpd_power_off_work_fn(struct work_struct *work)
210 struct generic_pm_domain *genpd;
212 genpd = container_of(work, struct generic_pm_domain, power_off_work);
215 mutex_lock(&genpd->parent->lock);
216 mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
217 pm_genpd_poweroff(genpd);
218 mutex_unlock(&genpd->lock);
220 mutex_unlock(&genpd->parent->lock);
224 * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
225 * @dev: Device to suspend.
227 * Carry out a runtime suspend of a device under the assumption that its
228 * pm_domain field points to the domain member of an object of type
229 * struct generic_pm_domain representing a PM domain consisting of I/O devices.
231 static int pm_genpd_runtime_suspend(struct device *dev)
233 struct generic_pm_domain *genpd;
235 dev_dbg(dev, "%s()\n", __func__);
237 genpd = dev_to_genpd(dev);
242 mutex_lock(&genpd->parent->lock);
243 mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
245 if (genpd->stop_device) {
246 int ret = genpd->stop_device(dev);
250 genpd->in_progress++;
251 pm_genpd_poweroff(genpd);
252 genpd->in_progress--;
255 mutex_unlock(&genpd->lock);
257 mutex_unlock(&genpd->parent->lock);
263 * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
264 * @dev: Device to resume.
266 * Carry out a runtime resume of a device under the assumption that its
267 * pm_domain field points to the domain member of an object of type
268 * struct generic_pm_domain representing a PM domain consisting of I/O devices.
270 static int pm_genpd_runtime_resume(struct device *dev)
272 struct generic_pm_domain *genpd;
273 struct dev_list_entry *dle;
276 dev_dbg(dev, "%s()\n", __func__);
278 genpd = dev_to_genpd(dev);
282 ret = pm_genpd_poweron(genpd);
286 mutex_lock(&genpd->lock);
288 list_for_each_entry(dle, &genpd->dev_list, node) {
289 if (dle->dev == dev) {
290 __pm_genpd_restore_device(dle, genpd);
295 if (genpd->start_device)
296 genpd->start_device(dev);
298 mutex_unlock(&genpd->lock);
305 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
307 #define pm_genpd_runtime_suspend NULL
308 #define pm_genpd_runtime_resume NULL
310 #endif /* CONFIG_PM_RUNTIME */
313 * pm_genpd_add_device - Add a device to an I/O PM domain.
314 * @genpd: PM domain to add the device to.
315 * @dev: Device to be added.
317 int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
319 struct dev_list_entry *dle;
322 dev_dbg(dev, "%s()\n", __func__);
324 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
327 mutex_lock(&genpd->lock);
329 if (genpd->power_is_off) {
334 list_for_each_entry(dle, &genpd->dev_list, node)
335 if (dle->dev == dev) {
340 dle = kzalloc(sizeof(*dle), GFP_KERNEL);
347 dle->need_restore = false;
348 list_add_tail(&dle->node, &genpd->dev_list);
350 spin_lock_irq(&dev->power.lock);
351 dev->pm_domain = &genpd->domain;
352 spin_unlock_irq(&dev->power.lock);
355 mutex_unlock(&genpd->lock);
361 * pm_genpd_remove_device - Remove a device from an I/O PM domain.
362 * @genpd: PM domain to remove the device from.
363 * @dev: Device to be removed.
365 int pm_genpd_remove_device(struct generic_pm_domain *genpd,
368 struct dev_list_entry *dle;
371 dev_dbg(dev, "%s()\n", __func__);
373 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
376 mutex_lock(&genpd->lock);
378 list_for_each_entry(dle, &genpd->dev_list, node) {
382 spin_lock_irq(&dev->power.lock);
383 dev->pm_domain = NULL;
384 spin_unlock_irq(&dev->power.lock);
386 list_del(&dle->node);
393 mutex_unlock(&genpd->lock);
399 * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
400 * @genpd: Master PM domain to add the subdomain to.
401 * @new_subdomain: Subdomain to be added.
403 int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
404 struct generic_pm_domain *new_subdomain)
406 struct generic_pm_domain *subdomain;
409 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
412 mutex_lock(&genpd->lock);
414 if (genpd->power_is_off && !new_subdomain->power_is_off) {
419 list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
420 if (subdomain == new_subdomain) {
426 mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
428 list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
429 new_subdomain->parent = genpd;
430 if (!subdomain->power_is_off)
433 mutex_unlock(&new_subdomain->lock);
436 mutex_unlock(&genpd->lock);
442 * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
443 * @genpd: Master PM domain to remove the subdomain from.
444 * @target: Subdomain to be removed.
446 int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
447 struct generic_pm_domain *target)
449 struct generic_pm_domain *subdomain;
452 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
455 mutex_lock(&genpd->lock);
457 list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
458 if (subdomain != target)
461 mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
463 list_del(&subdomain->sd_node);
464 subdomain->parent = NULL;
465 if (!subdomain->power_is_off)
466 genpd_sd_counter_dec(genpd);
468 mutex_unlock(&subdomain->lock);
474 mutex_unlock(&genpd->lock);
480 * pm_genpd_init - Initialize a generic I/O PM domain object.
481 * @genpd: PM domain object to initialize.
482 * @gov: PM domain governor to associate with the domain (may be NULL).
483 * @is_off: Initial value of the domain's power_is_off field.
485 void pm_genpd_init(struct generic_pm_domain *genpd,
486 struct dev_power_governor *gov, bool is_off)
488 if (IS_ERR_OR_NULL(genpd))
491 INIT_LIST_HEAD(&genpd->sd_node);
492 genpd->parent = NULL;
493 INIT_LIST_HEAD(&genpd->dev_list);
494 INIT_LIST_HEAD(&genpd->sd_list);
495 mutex_init(&genpd->lock);
497 INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
498 genpd->in_progress = 0;
500 genpd->power_is_off = is_off;
501 genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
502 genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
503 genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;