1f721889fSRafael J. Wysocki /* 2f721889fSRafael J. Wysocki * drivers/base/power/domain.c - Common code related to device power domains. 3f721889fSRafael J. Wysocki * 4f721889fSRafael J. Wysocki * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. 5f721889fSRafael J. Wysocki * 6f721889fSRafael J. Wysocki * This file is released under the GPLv2. 7f721889fSRafael J. Wysocki */ 8f721889fSRafael J. Wysocki 9f721889fSRafael J. Wysocki #include <linux/init.h> 10f721889fSRafael J. Wysocki #include <linux/kernel.h> 11f721889fSRafael J. Wysocki #include <linux/io.h> 12f721889fSRafael J. Wysocki #include <linux/pm_runtime.h> 13f721889fSRafael J. Wysocki #include <linux/pm_domain.h> 14f721889fSRafael J. Wysocki #include <linux/slab.h> 15f721889fSRafael J. Wysocki #include <linux/err.h> 1617b75ecaSRafael J. Wysocki #include <linux/sched.h> 1717b75ecaSRafael J. Wysocki #include <linux/suspend.h> 18f721889fSRafael J. Wysocki 195125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list); 205125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock); 215125bbf3SRafael J. Wysocki 225248051bSRafael J. Wysocki #ifdef CONFIG_PM 235248051bSRafael J. Wysocki 245248051bSRafael J. Wysocki static struct generic_pm_domain *dev_to_genpd(struct device *dev) 255248051bSRafael J. Wysocki { 265248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 275248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 285248051bSRafael J. Wysocki 29596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 305248051bSRafael J. Wysocki } 31f721889fSRafael J. Wysocki 32f721889fSRafael J. Wysocki static void genpd_sd_counter_dec(struct generic_pm_domain *genpd) 33f721889fSRafael J. Wysocki { 34f721889fSRafael J. Wysocki if (!WARN_ON(genpd->sd_count == 0)) 35f721889fSRafael J. Wysocki genpd->sd_count--; 36f721889fSRafael J. Wysocki } 37f721889fSRafael J. Wysocki 3817b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd) 3917b75ecaSRafael J. Wysocki { 4017b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 4117b75ecaSRafael J. Wysocki 4217b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 4317b75ecaSRafael J. Wysocki /* 4417b75ecaSRafael J. Wysocki * Wait for the domain to transition into either the active, 4517b75ecaSRafael J. Wysocki * or the power off state. 4617b75ecaSRafael J. Wysocki */ 4717b75ecaSRafael J. Wysocki for (;;) { 4817b75ecaSRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 4917b75ecaSRafael J. Wysocki TASK_UNINTERRUPTIBLE); 50c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 51c6d22b37SRafael J. Wysocki || genpd->status == GPD_STATE_POWER_OFF) 5217b75ecaSRafael J. Wysocki break; 5317b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 5417b75ecaSRafael J. Wysocki 5517b75ecaSRafael J. Wysocki schedule(); 5617b75ecaSRafael J. Wysocki 5717b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 5817b75ecaSRafael J. Wysocki } 5917b75ecaSRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 6017b75ecaSRafael J. Wysocki } 6117b75ecaSRafael J. Wysocki 6217b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd) 6317b75ecaSRafael J. Wysocki { 6417b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 6517b75ecaSRafael J. Wysocki } 6617b75ecaSRafael J. Wysocki 67c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd) 68c6d22b37SRafael J. Wysocki { 69c6d22b37SRafael J. Wysocki if (genpd->resume_count == 0) 70c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 71c6d22b37SRafael J. Wysocki } 72c6d22b37SRafael J. Wysocki 73f721889fSRafael J. Wysocki /** 745248051bSRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its parents. 755248051bSRafael J. Wysocki * @genpd: PM domain to power up. 765248051bSRafael J. Wysocki * 775248051bSRafael J. Wysocki * Restore power to @genpd and all of its parents so that it is possible to 785248051bSRafael J. Wysocki * resume a device belonging to it. 795248051bSRafael J. Wysocki */ 8018b4f3f5SMagnus Damm int pm_genpd_poweron(struct generic_pm_domain *genpd) 815248051bSRafael J. Wysocki { 8217b75ecaSRafael J. Wysocki struct generic_pm_domain *parent = genpd->parent; 835248051bSRafael J. Wysocki int ret = 0; 845248051bSRafael J. Wysocki 855248051bSRafael J. Wysocki start: 8617b75ecaSRafael J. Wysocki if (parent) { 87c6d22b37SRafael J. Wysocki genpd_acquire_lock(parent); 885248051bSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 8917b75ecaSRafael J. Wysocki } else { 9017b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 9117b75ecaSRafael J. Wysocki } 9217b75ecaSRafael J. Wysocki 9317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 94596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 955248051bSRafael J. Wysocki goto out; 965248051bSRafael J. Wysocki 97c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 98c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 99c6d22b37SRafael J. Wysocki goto out; 100c6d22b37SRafael J. Wysocki } 101c6d22b37SRafael J. Wysocki 10217b75ecaSRafael J. Wysocki if (parent && parent->status != GPD_STATE_ACTIVE) { 1035248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 104c6d22b37SRafael J. Wysocki genpd_release_lock(parent); 1055248051bSRafael J. Wysocki 10617b75ecaSRafael J. Wysocki ret = pm_genpd_poweron(parent); 1075248051bSRafael J. Wysocki if (ret) 1085248051bSRafael J. Wysocki return ret; 1095248051bSRafael J. Wysocki 1105248051bSRafael J. Wysocki goto start; 1115248051bSRafael J. Wysocki } 1125248051bSRafael J. Wysocki 1135248051bSRafael J. Wysocki if (genpd->power_on) { 114fe202fdeSRafael J. Wysocki ret = genpd->power_on(genpd); 1155248051bSRafael J. Wysocki if (ret) 1165248051bSRafael J. Wysocki goto out; 1175248051bSRafael J. Wysocki } 1185248051bSRafael J. Wysocki 119c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 12017b75ecaSRafael J. Wysocki if (parent) 12117b75ecaSRafael J. Wysocki parent->sd_count++; 1225248051bSRafael J. Wysocki 1235248051bSRafael J. Wysocki out: 1245248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 12517b75ecaSRafael J. Wysocki if (parent) 126c6d22b37SRafael J. Wysocki genpd_release_lock(parent); 1275248051bSRafael J. Wysocki 1285248051bSRafael J. Wysocki return ret; 1295248051bSRafael J. Wysocki } 1305248051bSRafael J. Wysocki 1315248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 1325248051bSRafael J. Wysocki 1335248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 1345248051bSRafael J. Wysocki 1355248051bSRafael J. Wysocki /** 136f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 137f721889fSRafael J. Wysocki * @dle: Device list entry of the device to save the state of. 138f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 139f721889fSRafael J. Wysocki */ 140f721889fSRafael J. Wysocki static int __pm_genpd_save_device(struct dev_list_entry *dle, 141f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 14217b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 143f721889fSRafael J. Wysocki { 144f721889fSRafael J. Wysocki struct device *dev = dle->dev; 145f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 146f721889fSRafael J. Wysocki int ret = 0; 147f721889fSRafael J. Wysocki 148f721889fSRafael J. Wysocki if (dle->need_restore) 149f721889fSRafael J. Wysocki return 0; 150f721889fSRafael J. Wysocki 15117b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 15217b75ecaSRafael J. Wysocki 153f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_suspend) { 154f721889fSRafael J. Wysocki if (genpd->start_device) 155f721889fSRafael J. Wysocki genpd->start_device(dev); 156f721889fSRafael J. Wysocki 157f721889fSRafael J. Wysocki ret = drv->pm->runtime_suspend(dev); 158f721889fSRafael J. Wysocki 159f721889fSRafael J. Wysocki if (genpd->stop_device) 160f721889fSRafael J. Wysocki genpd->stop_device(dev); 161f721889fSRafael J. Wysocki } 162f721889fSRafael J. Wysocki 16317b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 16417b75ecaSRafael J. Wysocki 165f721889fSRafael J. Wysocki if (!ret) 166f721889fSRafael J. Wysocki dle->need_restore = true; 167f721889fSRafael J. Wysocki 168f721889fSRafael J. Wysocki return ret; 169f721889fSRafael J. Wysocki } 170f721889fSRafael J. Wysocki 171f721889fSRafael J. Wysocki /** 172f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 173f721889fSRafael J. Wysocki * @dle: Device list entry of the device to restore the state of. 174f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 175f721889fSRafael J. Wysocki */ 176f721889fSRafael J. Wysocki static void __pm_genpd_restore_device(struct dev_list_entry *dle, 177f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 17817b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 179f721889fSRafael J. Wysocki { 180f721889fSRafael J. Wysocki struct device *dev = dle->dev; 181f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 182f721889fSRafael J. Wysocki 183f721889fSRafael J. Wysocki if (!dle->need_restore) 184f721889fSRafael J. Wysocki return; 185f721889fSRafael J. Wysocki 18617b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 18717b75ecaSRafael J. Wysocki 188f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_resume) { 189f721889fSRafael J. Wysocki if (genpd->start_device) 190f721889fSRafael J. Wysocki genpd->start_device(dev); 191f721889fSRafael J. Wysocki 192f721889fSRafael J. Wysocki drv->pm->runtime_resume(dev); 193f721889fSRafael J. Wysocki 194f721889fSRafael J. Wysocki if (genpd->stop_device) 195f721889fSRafael J. Wysocki genpd->stop_device(dev); 196f721889fSRafael J. Wysocki } 197f721889fSRafael J. Wysocki 19817b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 19917b75ecaSRafael J. Wysocki 200f721889fSRafael J. Wysocki dle->need_restore = false; 201f721889fSRafael J. Wysocki } 202f721889fSRafael J. Wysocki 203f721889fSRafael J. Wysocki /** 204c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 205c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 206c6d22b37SRafael J. Wysocki * 207c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 208c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 209c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 210c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 211c6d22b37SRafael J. Wysocki */ 212c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 213c6d22b37SRafael J. Wysocki { 214c6d22b37SRafael J. Wysocki return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 215c6d22b37SRafael J. Wysocki } 216c6d22b37SRafael J. Wysocki 217c6d22b37SRafael J. Wysocki /** 21856375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 21956375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 22056375fd4SRafael J. Wysocki * 22156375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 22256375fd4SRafael J. Wysocki * before. 22356375fd4SRafael J. Wysocki */ 2240bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 22556375fd4SRafael J. Wysocki { 22656375fd4SRafael J. Wysocki if (!work_pending(&genpd->power_off_work)) 22756375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 22856375fd4SRafael J. Wysocki } 22956375fd4SRafael J. Wysocki 23056375fd4SRafael J. Wysocki /** 231f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 232f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 233f721889fSRafael J. Wysocki * 234f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 235f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 236f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 237f721889fSRafael J. Wysocki */ 238f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 23917b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 240f721889fSRafael J. Wysocki { 241f721889fSRafael J. Wysocki struct generic_pm_domain *parent; 242f721889fSRafael J. Wysocki struct dev_list_entry *dle; 243f721889fSRafael J. Wysocki unsigned int not_suspended; 244c6d22b37SRafael J. Wysocki int ret = 0; 245f721889fSRafael J. Wysocki 246c6d22b37SRafael J. Wysocki start: 247c6d22b37SRafael J. Wysocki /* 248c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 249c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 250c6d22b37SRafael J. Wysocki * (2) System suspend is in progress. 251c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 252c6d22b37SRafael J. Wysocki */ 253c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0 254c6d22b37SRafael J. Wysocki || genpd->resume_count > 0) 255f721889fSRafael J. Wysocki return 0; 256f721889fSRafael J. Wysocki 257f721889fSRafael J. Wysocki if (genpd->sd_count > 0) 258f721889fSRafael J. Wysocki return -EBUSY; 259f721889fSRafael J. Wysocki 260f721889fSRafael J. Wysocki not_suspended = 0; 261f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) 262f721889fSRafael J. Wysocki if (dle->dev->driver && !pm_runtime_suspended(dle->dev)) 263f721889fSRafael J. Wysocki not_suspended++; 264f721889fSRafael J. Wysocki 265f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 266f721889fSRafael J. Wysocki return -EBUSY; 267f721889fSRafael J. Wysocki 268c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 269c6d22b37SRafael J. Wysocki /* 270c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 271c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 272c6d22b37SRafael J. Wysocki */ 273c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 274c6d22b37SRafael J. Wysocki return 0; 275c6d22b37SRafael J. Wysocki } 276c6d22b37SRafael J. Wysocki 277f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 278f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 279f721889fSRafael J. Wysocki return -EAGAIN; 280f721889fSRafael J. Wysocki } 281f721889fSRafael J. Wysocki 28217b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 283c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 28417b75ecaSRafael J. Wysocki 285f721889fSRafael J. Wysocki list_for_each_entry_reverse(dle, &genpd->dev_list, node) { 286f721889fSRafael J. Wysocki ret = __pm_genpd_save_device(dle, genpd); 287697a7f37SRafael J. Wysocki if (ret) { 288697a7f37SRafael J. Wysocki genpd_set_active(genpd); 289697a7f37SRafael J. Wysocki goto out; 290697a7f37SRafael J. Wysocki } 291f721889fSRafael J. Wysocki 292c6d22b37SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 293c6d22b37SRafael J. Wysocki goto out; 294c6d22b37SRafael J. Wysocki 295c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 296c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 297c6d22b37SRafael J. Wysocki goto start; 298c6d22b37SRafael J. Wysocki } 299c6d22b37SRafael J. Wysocki } 30017b75ecaSRafael J. Wysocki 30117b75ecaSRafael J. Wysocki parent = genpd->parent; 30217b75ecaSRafael J. Wysocki if (parent) { 303c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 304c6d22b37SRafael J. Wysocki 30517b75ecaSRafael J. Wysocki genpd_acquire_lock(parent); 30617b75ecaSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 307c6d22b37SRafael J. Wysocki 308c6d22b37SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) { 309c6d22b37SRafael J. Wysocki genpd_release_lock(parent); 310c6d22b37SRafael J. Wysocki goto out; 311c6d22b37SRafael J. Wysocki } 31217b75ecaSRafael J. Wysocki } 31317b75ecaSRafael J. Wysocki 314d2805402SRafael J. Wysocki if (genpd->power_off) { 315d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 316d2805402SRafael J. Wysocki if (ret == -EBUSY) { 317d2805402SRafael J. Wysocki genpd_set_active(genpd); 318d2805402SRafael J. Wysocki if (parent) 319d2805402SRafael J. Wysocki genpd_release_lock(parent); 320d2805402SRafael J. Wysocki 321d2805402SRafael J. Wysocki goto out; 322d2805402SRafael J. Wysocki } 323d2805402SRafael J. Wysocki } 324f721889fSRafael J. Wysocki 32517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 326f721889fSRafael J. Wysocki 327f721889fSRafael J. Wysocki if (parent) { 328f721889fSRafael J. Wysocki genpd_sd_counter_dec(parent); 329f721889fSRafael J. Wysocki if (parent->sd_count == 0) 33056375fd4SRafael J. Wysocki genpd_queue_power_off_work(parent); 33117b75ecaSRafael J. Wysocki 33217b75ecaSRafael J. Wysocki genpd_release_lock(parent); 333f721889fSRafael J. Wysocki } 334f721889fSRafael J. Wysocki 335c6d22b37SRafael J. Wysocki out: 336c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 337c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 338c6d22b37SRafael J. Wysocki return ret; 339f721889fSRafael J. Wysocki } 340f721889fSRafael J. Wysocki 341f721889fSRafael J. Wysocki /** 342f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 343f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 344f721889fSRafael J. Wysocki */ 345f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 346f721889fSRafael J. Wysocki { 347f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 348f721889fSRafael J. Wysocki 349f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 350f721889fSRafael J. Wysocki 35117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 352f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 35317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 354f721889fSRafael J. Wysocki } 355f721889fSRafael J. Wysocki 356f721889fSRafael J. Wysocki /** 357f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 358f721889fSRafael J. Wysocki * @dev: Device to suspend. 359f721889fSRafael J. Wysocki * 360f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 361f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 362f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 363f721889fSRafael J. Wysocki */ 364f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 365f721889fSRafael J. Wysocki { 366f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 367f721889fSRafael J. Wysocki 368f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 369f721889fSRafael J. Wysocki 3705248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 3715248051bSRafael J. Wysocki if (IS_ERR(genpd)) 372f721889fSRafael J. Wysocki return -EINVAL; 373f721889fSRafael J. Wysocki 374f721889fSRafael J. Wysocki if (genpd->stop_device) { 375f721889fSRafael J. Wysocki int ret = genpd->stop_device(dev); 376f721889fSRafael J. Wysocki if (ret) 37717b75ecaSRafael J. Wysocki return ret; 378f721889fSRafael J. Wysocki } 37917b75ecaSRafael J. Wysocki 380c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 381f721889fSRafael J. Wysocki genpd->in_progress++; 382f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 383f721889fSRafael J. Wysocki genpd->in_progress--; 384c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 385f721889fSRafael J. Wysocki 386f721889fSRafael J. Wysocki return 0; 387f721889fSRafael J. Wysocki } 388f721889fSRafael J. Wysocki 389f721889fSRafael J. Wysocki /** 390596ba34bSRafael J. Wysocki * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 391596ba34bSRafael J. Wysocki * @dev: Device to resume. 392596ba34bSRafael J. Wysocki * @genpd: PM domain the device belongs to. 393596ba34bSRafael J. Wysocki */ 394596ba34bSRafael J. Wysocki static void __pm_genpd_runtime_resume(struct device *dev, 395596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd) 396596ba34bSRafael J. Wysocki { 397596ba34bSRafael J. Wysocki struct dev_list_entry *dle; 398596ba34bSRafael J. Wysocki 399596ba34bSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) { 400596ba34bSRafael J. Wysocki if (dle->dev == dev) { 401596ba34bSRafael J. Wysocki __pm_genpd_restore_device(dle, genpd); 402596ba34bSRafael J. Wysocki break; 403596ba34bSRafael J. Wysocki } 404596ba34bSRafael J. Wysocki } 405596ba34bSRafael J. Wysocki } 406596ba34bSRafael J. Wysocki 407596ba34bSRafael J. Wysocki /** 408f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 409f721889fSRafael J. Wysocki * @dev: Device to resume. 410f721889fSRafael J. Wysocki * 411f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 412f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 413f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 414f721889fSRafael J. Wysocki */ 415f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 416f721889fSRafael J. Wysocki { 417f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 418c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 419f721889fSRafael J. Wysocki int ret; 420f721889fSRafael J. Wysocki 421f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 422f721889fSRafael J. Wysocki 4235248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4245248051bSRafael J. Wysocki if (IS_ERR(genpd)) 425f721889fSRafael J. Wysocki return -EINVAL; 426f721889fSRafael J. Wysocki 427f721889fSRafael J. Wysocki ret = pm_genpd_poweron(genpd); 428f721889fSRafael J. Wysocki if (ret) 429f721889fSRafael J. Wysocki return ret; 430f721889fSRafael J. Wysocki 431c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 43217b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 433c6d22b37SRafael J. Wysocki genpd->resume_count++; 434c6d22b37SRafael J. Wysocki for (;;) { 435c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 436c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 437c6d22b37SRafael J. Wysocki /* 438c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 439c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 440c6d22b37SRafael J. Wysocki * not wait. 441c6d22b37SRafael J. Wysocki */ 442c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 443c6d22b37SRafael J. Wysocki break; 444c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 445c6d22b37SRafael J. Wysocki 446c6d22b37SRafael J. Wysocki schedule(); 447c6d22b37SRafael J. Wysocki 448c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 449c6d22b37SRafael J. Wysocki } 450c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 451596ba34bSRafael J. Wysocki __pm_genpd_runtime_resume(dev, genpd); 452c6d22b37SRafael J. Wysocki genpd->resume_count--; 453c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 45417b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 455c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 45617b75ecaSRafael J. Wysocki 45717b75ecaSRafael J. Wysocki if (genpd->start_device) 45817b75ecaSRafael J. Wysocki genpd->start_device(dev); 459f721889fSRafael J. Wysocki 460f721889fSRafael J. Wysocki return 0; 461f721889fSRafael J. Wysocki } 462f721889fSRafael J. Wysocki 463f721889fSRafael J. Wysocki #else 464f721889fSRafael J. Wysocki 465f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 466596ba34bSRafael J. Wysocki static inline void __pm_genpd_runtime_resume(struct device *dev, 467596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd) {} 468f721889fSRafael J. Wysocki 469f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 470f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 471f721889fSRafael J. Wysocki 472f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 473f721889fSRafael J. Wysocki 474596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 475596ba34bSRafael J. Wysocki 476596ba34bSRafael J. Wysocki /** 477596ba34bSRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents. 478596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 479596ba34bSRafael J. Wysocki * 480596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 481596ba34bSRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its parent. 482596ba34bSRafael J. Wysocki * 483596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 484596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 485596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 486596ba34bSRafael J. Wysocki */ 487596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 488596ba34bSRafael J. Wysocki { 489596ba34bSRafael J. Wysocki struct generic_pm_domain *parent = genpd->parent; 490596ba34bSRafael J. Wysocki 49117b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 492596ba34bSRafael J. Wysocki return; 493596ba34bSRafael J. Wysocki 494596ba34bSRafael J. Wysocki if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0) 495596ba34bSRafael J. Wysocki return; 496596ba34bSRafael J. Wysocki 497596ba34bSRafael J. Wysocki if (genpd->power_off) 498596ba34bSRafael J. Wysocki genpd->power_off(genpd); 499596ba34bSRafael J. Wysocki 50017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 501596ba34bSRafael J. Wysocki if (parent) { 502596ba34bSRafael J. Wysocki genpd_sd_counter_dec(parent); 503596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(parent); 504596ba34bSRafael J. Wysocki } 505596ba34bSRafael J. Wysocki } 506596ba34bSRafael J. Wysocki 507596ba34bSRafael J. Wysocki /** 5084ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 5094ecd6e65SRafael J. Wysocki * @dev: Device to check. 5104ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 5114ecd6e65SRafael J. Wysocki * 5124ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 5134ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 5144ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 5154ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 5164ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 5174ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 5184ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 5194ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 5204ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 5214ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 5224ecd6e65SRafael J. Wysocki */ 5234ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 5244ecd6e65SRafael J. Wysocki { 5254ecd6e65SRafael J. Wysocki bool active_wakeup; 5264ecd6e65SRafael J. Wysocki 5274ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 5284ecd6e65SRafael J. Wysocki return false; 5294ecd6e65SRafael J. Wysocki 5304ecd6e65SRafael J. Wysocki active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev); 5314ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 5324ecd6e65SRafael J. Wysocki } 5334ecd6e65SRafael J. Wysocki 5344ecd6e65SRafael J. Wysocki /** 535596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 536596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 537596ba34bSRafael J. Wysocki * 538596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 539596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 540596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 541596ba34bSRafael J. Wysocki * consisting of I/O devices. 542596ba34bSRafael J. Wysocki */ 543596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 544596ba34bSRafael J. Wysocki { 545596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 546b6c10c84SRafael J. Wysocki int ret; 547596ba34bSRafael J. Wysocki 548596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 549596ba34bSRafael J. Wysocki 550596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 551596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 552596ba34bSRafael J. Wysocki return -EINVAL; 553596ba34bSRafael J. Wysocki 55417b75ecaSRafael J. Wysocki /* 55517b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 55617b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 55717b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 55817b75ecaSRafael J. Wysocki */ 55917b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 56017b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 56117b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 56217b75ecaSRafael J. Wysocki 56317b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 56417b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 56517b75ecaSRafael J. Wysocki return -EBUSY; 56617b75ecaSRafael J. Wysocki } 56717b75ecaSRafael J. Wysocki 5684ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 5694ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 5704ecd6e65SRafael J. Wysocki 57117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 572596ba34bSRafael J. Wysocki 573596ba34bSRafael J. Wysocki if (genpd->prepared_count++ == 0) 57417b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 57517b75ecaSRafael J. Wysocki 57617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 577596ba34bSRafael J. Wysocki 578596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 57917b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 580596ba34bSRafael J. Wysocki return 0; 581596ba34bSRafael J. Wysocki } 582596ba34bSRafael J. Wysocki 583596ba34bSRafael J. Wysocki /* 58417b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 58517b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 58617b75ecaSRafael J. Wysocki * is suspended (e.g. it's been stopped by .stop_device()), we need 58717b75ecaSRafael J. Wysocki * to make it operational. 588596ba34bSRafael J. Wysocki */ 58917b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 590596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 591596ba34bSRafael J. Wysocki 592b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 593b6c10c84SRafael J. Wysocki if (ret) { 594b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 595b6c10c84SRafael J. Wysocki 596b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 597b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 598b6c10c84SRafael J. Wysocki 599b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 60017b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 601b6c10c84SRafael J. Wysocki } 60217b75ecaSRafael J. Wysocki 60317b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 604b6c10c84SRafael J. Wysocki return ret; 605596ba34bSRafael J. Wysocki } 606596ba34bSRafael J. Wysocki 607596ba34bSRafael J. Wysocki /** 608596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 609596ba34bSRafael J. Wysocki * @dev: Device to suspend. 610596ba34bSRafael J. Wysocki * 611596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 612596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 613596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 614596ba34bSRafael J. Wysocki */ 615596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 616596ba34bSRafael J. Wysocki { 617596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 618596ba34bSRafael J. Wysocki 619596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 620596ba34bSRafael J. Wysocki 621596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 622596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 623596ba34bSRafael J. Wysocki return -EINVAL; 624596ba34bSRafael J. Wysocki 625596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 626596ba34bSRafael J. Wysocki } 627596ba34bSRafael J. Wysocki 628596ba34bSRafael J. Wysocki /** 629596ba34bSRafael J. Wysocki * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain. 630596ba34bSRafael J. Wysocki * @dev: Device to suspend. 631596ba34bSRafael J. Wysocki * 632596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 633596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 634596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 635596ba34bSRafael J. Wysocki */ 636596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 637596ba34bSRafael J. Wysocki { 638596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 639596ba34bSRafael J. Wysocki int ret; 640596ba34bSRafael J. Wysocki 641596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 642596ba34bSRafael J. Wysocki 643596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 644596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 645596ba34bSRafael J. Wysocki return -EINVAL; 646596ba34bSRafael J. Wysocki 647596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 648596ba34bSRafael J. Wysocki return 0; 649596ba34bSRafael J. Wysocki 650596ba34bSRafael J. Wysocki ret = pm_generic_suspend_noirq(dev); 651596ba34bSRafael J. Wysocki if (ret) 652596ba34bSRafael J. Wysocki return ret; 653596ba34bSRafael J. Wysocki 654d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 655d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 656d4f2d87aSRafael J. Wysocki return 0; 657d4f2d87aSRafael J. Wysocki 658596ba34bSRafael J. Wysocki if (genpd->stop_device) 659596ba34bSRafael J. Wysocki genpd->stop_device(dev); 660596ba34bSRafael J. Wysocki 661596ba34bSRafael J. Wysocki /* 662596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 663596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 664596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 665596ba34bSRafael J. Wysocki */ 666596ba34bSRafael J. Wysocki genpd->suspended_count++; 667596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 668596ba34bSRafael J. Wysocki 669596ba34bSRafael J. Wysocki return 0; 670596ba34bSRafael J. Wysocki } 671596ba34bSRafael J. Wysocki 672596ba34bSRafael J. Wysocki /** 673596ba34bSRafael J. Wysocki * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain. 674596ba34bSRafael J. Wysocki * @dev: Device to resume. 675596ba34bSRafael J. Wysocki * 676596ba34bSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 677596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 678596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 679596ba34bSRafael J. Wysocki * devices. 680596ba34bSRafael J. Wysocki */ 681596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 682596ba34bSRafael J. Wysocki { 683596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 684596ba34bSRafael J. Wysocki 685596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 686596ba34bSRafael J. Wysocki 687596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 688596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 689596ba34bSRafael J. Wysocki return -EINVAL; 690596ba34bSRafael J. Wysocki 691596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 692596ba34bSRafael J. Wysocki return 0; 693596ba34bSRafael J. Wysocki 694596ba34bSRafael J. Wysocki /* 695596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 696596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 697596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 698596ba34bSRafael J. Wysocki */ 699596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 700596ba34bSRafael J. Wysocki genpd->suspended_count--; 701596ba34bSRafael J. Wysocki if (genpd->start_device) 702596ba34bSRafael J. Wysocki genpd->start_device(dev); 703596ba34bSRafael J. Wysocki 704596ba34bSRafael J. Wysocki return pm_generic_resume_noirq(dev); 705596ba34bSRafael J. Wysocki } 706596ba34bSRafael J. Wysocki 707596ba34bSRafael J. Wysocki /** 708596ba34bSRafael J. Wysocki * pm_genpd_resume - Resume a device belonging to an I/O power domain. 709596ba34bSRafael J. Wysocki * @dev: Device to resume. 710596ba34bSRafael J. Wysocki * 711596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 712596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 713596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 714596ba34bSRafael J. Wysocki */ 715596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 716596ba34bSRafael J. Wysocki { 717596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 718596ba34bSRafael J. Wysocki 719596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 720596ba34bSRafael J. Wysocki 721596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 722596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 723596ba34bSRafael J. Wysocki return -EINVAL; 724596ba34bSRafael J. Wysocki 725596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 726596ba34bSRafael J. Wysocki } 727596ba34bSRafael J. Wysocki 728596ba34bSRafael J. Wysocki /** 729596ba34bSRafael J. Wysocki * pm_genpd_freeze - Freeze a device belonging to an I/O power domain. 730596ba34bSRafael J. Wysocki * @dev: Device to freeze. 731596ba34bSRafael J. Wysocki * 732596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 733596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 734596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 735596ba34bSRafael J. Wysocki */ 736596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 737596ba34bSRafael J. Wysocki { 738596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 739596ba34bSRafael J. Wysocki 740596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 741596ba34bSRafael J. Wysocki 742596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 743596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 744596ba34bSRafael J. Wysocki return -EINVAL; 745596ba34bSRafael J. Wysocki 746596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 747596ba34bSRafael J. Wysocki } 748596ba34bSRafael J. Wysocki 749596ba34bSRafael J. Wysocki /** 750596ba34bSRafael J. Wysocki * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain. 751596ba34bSRafael J. Wysocki * @dev: Device to freeze. 752596ba34bSRafael J. Wysocki * 753596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 754596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 755596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 756596ba34bSRafael J. Wysocki * devices. 757596ba34bSRafael J. Wysocki */ 758596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 759596ba34bSRafael J. Wysocki { 760596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 761596ba34bSRafael J. Wysocki int ret; 762596ba34bSRafael J. Wysocki 763596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 764596ba34bSRafael J. Wysocki 765596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 766596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 767596ba34bSRafael J. Wysocki return -EINVAL; 768596ba34bSRafael J. Wysocki 769596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 770596ba34bSRafael J. Wysocki return 0; 771596ba34bSRafael J. Wysocki 772596ba34bSRafael J. Wysocki ret = pm_generic_freeze_noirq(dev); 773596ba34bSRafael J. Wysocki if (ret) 774596ba34bSRafael J. Wysocki return ret; 775596ba34bSRafael J. Wysocki 776596ba34bSRafael J. Wysocki if (genpd->stop_device) 777596ba34bSRafael J. Wysocki genpd->stop_device(dev); 778596ba34bSRafael J. Wysocki 779596ba34bSRafael J. Wysocki return 0; 780596ba34bSRafael J. Wysocki } 781596ba34bSRafael J. Wysocki 782596ba34bSRafael J. Wysocki /** 783596ba34bSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain. 784596ba34bSRafael J. Wysocki * @dev: Device to thaw. 785596ba34bSRafael J. Wysocki * 786596ba34bSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 787596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 788596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 789596ba34bSRafael J. Wysocki * devices. 790596ba34bSRafael J. Wysocki */ 791596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 792596ba34bSRafael J. Wysocki { 793596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 794596ba34bSRafael J. Wysocki 795596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 796596ba34bSRafael J. Wysocki 797596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 798596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 799596ba34bSRafael J. Wysocki return -EINVAL; 800596ba34bSRafael J. Wysocki 801596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 802596ba34bSRafael J. Wysocki return 0; 803596ba34bSRafael J. Wysocki 804596ba34bSRafael J. Wysocki if (genpd->start_device) 805596ba34bSRafael J. Wysocki genpd->start_device(dev); 806596ba34bSRafael J. Wysocki 807596ba34bSRafael J. Wysocki return pm_generic_thaw_noirq(dev); 808596ba34bSRafael J. Wysocki } 809596ba34bSRafael J. Wysocki 810596ba34bSRafael J. Wysocki /** 811596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 812596ba34bSRafael J. Wysocki * @dev: Device to thaw. 813596ba34bSRafael J. Wysocki * 814596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 815596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 816596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 817596ba34bSRafael J. Wysocki */ 818596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 819596ba34bSRafael J. Wysocki { 820596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 821596ba34bSRafael J. Wysocki 822596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 823596ba34bSRafael J. Wysocki 824596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 825596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 826596ba34bSRafael J. Wysocki return -EINVAL; 827596ba34bSRafael J. Wysocki 828596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 829596ba34bSRafael J. Wysocki } 830596ba34bSRafael J. Wysocki 831596ba34bSRafael J. Wysocki /** 832596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain. 833596ba34bSRafael J. Wysocki * @dev: Device to suspend. 834596ba34bSRafael J. Wysocki * 835596ba34bSRafael J. Wysocki * Power off a device under the assumption that its pm_domain field points to 836596ba34bSRafael J. Wysocki * the domain member of an object of type struct generic_pm_domain representing 837596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 838596ba34bSRafael J. Wysocki */ 839596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev) 840596ba34bSRafael J. Wysocki { 841596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 842596ba34bSRafael J. Wysocki 843596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 844596ba34bSRafael J. Wysocki 845596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 846596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 847596ba34bSRafael J. Wysocki return -EINVAL; 848596ba34bSRafael J. Wysocki 849596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev); 850596ba34bSRafael J. Wysocki } 851596ba34bSRafael J. Wysocki 852596ba34bSRafael J. Wysocki /** 853596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain. 854596ba34bSRafael J. Wysocki * @dev: Device to suspend. 855596ba34bSRafael J. Wysocki * 856596ba34bSRafael J. Wysocki * Carry out a late powering off of a device under the assumption that its 857596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 858596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 859596ba34bSRafael J. Wysocki */ 860596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev) 861596ba34bSRafael J. Wysocki { 862596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 863596ba34bSRafael J. Wysocki int ret; 864596ba34bSRafael J. Wysocki 865596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 866596ba34bSRafael J. Wysocki 867596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 868596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 869596ba34bSRafael J. Wysocki return -EINVAL; 870596ba34bSRafael J. Wysocki 871596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 872596ba34bSRafael J. Wysocki return 0; 873596ba34bSRafael J. Wysocki 874596ba34bSRafael J. Wysocki ret = pm_generic_poweroff_noirq(dev); 875596ba34bSRafael J. Wysocki if (ret) 876596ba34bSRafael J. Wysocki return ret; 877596ba34bSRafael J. Wysocki 878d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 879d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 880d4f2d87aSRafael J. Wysocki return 0; 881d4f2d87aSRafael J. Wysocki 882596ba34bSRafael J. Wysocki if (genpd->stop_device) 883596ba34bSRafael J. Wysocki genpd->stop_device(dev); 884596ba34bSRafael J. Wysocki 885596ba34bSRafael J. Wysocki /* 886596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 887596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 888596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 889596ba34bSRafael J. Wysocki */ 890596ba34bSRafael J. Wysocki genpd->suspended_count++; 891596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 892596ba34bSRafael J. Wysocki 893596ba34bSRafael J. Wysocki return 0; 894596ba34bSRafael J. Wysocki } 895596ba34bSRafael J. Wysocki 896596ba34bSRafael J. Wysocki /** 897596ba34bSRafael J. Wysocki * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain. 898596ba34bSRafael J. Wysocki * @dev: Device to resume. 899596ba34bSRafael J. Wysocki * 900596ba34bSRafael J. Wysocki * Carry out an early restore of a device under the assumption that its 901596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 902596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 903596ba34bSRafael J. Wysocki * devices. 904596ba34bSRafael J. Wysocki */ 905596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 906596ba34bSRafael J. Wysocki { 907596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 908596ba34bSRafael J. Wysocki 909596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 910596ba34bSRafael J. Wysocki 911596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 912596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 913596ba34bSRafael J. Wysocki return -EINVAL; 914596ba34bSRafael J. Wysocki 915596ba34bSRafael J. Wysocki /* 916596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 917596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 918596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 919596ba34bSRafael J. Wysocki */ 92017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 921596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 922596ba34bSRafael J. Wysocki /* 923596ba34bSRafael J. Wysocki * The boot kernel might put the domain into the power on state, 924596ba34bSRafael J. Wysocki * so make sure it really is powered off. 925596ba34bSRafael J. Wysocki */ 926596ba34bSRafael J. Wysocki if (genpd->power_off) 927596ba34bSRafael J. Wysocki genpd->power_off(genpd); 928596ba34bSRafael J. Wysocki return 0; 929596ba34bSRafael J. Wysocki } 930596ba34bSRafael J. Wysocki 931596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 932596ba34bSRafael J. Wysocki genpd->suspended_count--; 933596ba34bSRafael J. Wysocki if (genpd->start_device) 934596ba34bSRafael J. Wysocki genpd->start_device(dev); 935596ba34bSRafael J. Wysocki 936596ba34bSRafael J. Wysocki return pm_generic_restore_noirq(dev); 937596ba34bSRafael J. Wysocki } 938596ba34bSRafael J. Wysocki 939596ba34bSRafael J. Wysocki /** 940596ba34bSRafael J. Wysocki * pm_genpd_restore - Restore a device belonging to an I/O power domain. 941596ba34bSRafael J. Wysocki * @dev: Device to resume. 942596ba34bSRafael J. Wysocki * 943596ba34bSRafael J. Wysocki * Restore a device under the assumption that its pm_domain field points to the 944596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 945596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 946596ba34bSRafael J. Wysocki */ 947596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev) 948596ba34bSRafael J. Wysocki { 949596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 950596ba34bSRafael J. Wysocki 951596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 952596ba34bSRafael J. Wysocki 953596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 954596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 955596ba34bSRafael J. Wysocki return -EINVAL; 956596ba34bSRafael J. Wysocki 957596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_restore(dev); 958596ba34bSRafael J. Wysocki } 959596ba34bSRafael J. Wysocki 960596ba34bSRafael J. Wysocki /** 961596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 962596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 963596ba34bSRafael J. Wysocki * 964596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 965596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 966596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 967596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 968596ba34bSRafael J. Wysocki */ 969596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 970596ba34bSRafael J. Wysocki { 971596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 972596ba34bSRafael J. Wysocki bool run_complete; 973596ba34bSRafael J. Wysocki 974596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 975596ba34bSRafael J. Wysocki 976596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 977596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 978596ba34bSRafael J. Wysocki return; 979596ba34bSRafael J. Wysocki 980596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 981596ba34bSRafael J. Wysocki 982596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 983596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 984596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 985596ba34bSRafael J. Wysocki 986596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 987596ba34bSRafael J. Wysocki 988596ba34bSRafael J. Wysocki if (run_complete) { 989596ba34bSRafael J. Wysocki pm_generic_complete(dev); 9906f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 991596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 9926f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 993596ba34bSRafael J. Wysocki } 994596ba34bSRafael J. Wysocki } 995596ba34bSRafael J. Wysocki 996596ba34bSRafael J. Wysocki #else 997596ba34bSRafael J. Wysocki 998596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 999596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 1000596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 1001596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1002596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1003596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 1004596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 1005596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1006596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1007596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq NULL 1008596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff NULL 1009596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1010596ba34bSRafael J. Wysocki #define pm_genpd_restore NULL 1011596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1012596ba34bSRafael J. Wysocki 1013596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1014596ba34bSRafael J. Wysocki 1015f721889fSRafael J. Wysocki /** 1016f721889fSRafael J. Wysocki * pm_genpd_add_device - Add a device to an I/O PM domain. 1017f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1018f721889fSRafael J. Wysocki * @dev: Device to be added. 1019f721889fSRafael J. Wysocki */ 1020f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 1021f721889fSRafael J. Wysocki { 1022f721889fSRafael J. Wysocki struct dev_list_entry *dle; 1023f721889fSRafael J. Wysocki int ret = 0; 1024f721889fSRafael J. Wysocki 1025f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1026f721889fSRafael J. Wysocki 1027f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1028f721889fSRafael J. Wysocki return -EINVAL; 1029f721889fSRafael J. Wysocki 103017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1031f721889fSRafael J. Wysocki 103217b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) { 1033f721889fSRafael J. Wysocki ret = -EINVAL; 1034f721889fSRafael J. Wysocki goto out; 1035f721889fSRafael J. Wysocki } 1036f721889fSRafael J. Wysocki 1037596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1038596ba34bSRafael J. Wysocki ret = -EAGAIN; 1039596ba34bSRafael J. Wysocki goto out; 1040596ba34bSRafael J. Wysocki } 1041596ba34bSRafael J. Wysocki 1042f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) 1043f721889fSRafael J. Wysocki if (dle->dev == dev) { 1044f721889fSRafael J. Wysocki ret = -EINVAL; 1045f721889fSRafael J. Wysocki goto out; 1046f721889fSRafael J. Wysocki } 1047f721889fSRafael J. Wysocki 1048f721889fSRafael J. Wysocki dle = kzalloc(sizeof(*dle), GFP_KERNEL); 1049f721889fSRafael J. Wysocki if (!dle) { 1050f721889fSRafael J. Wysocki ret = -ENOMEM; 1051f721889fSRafael J. Wysocki goto out; 1052f721889fSRafael J. Wysocki } 1053f721889fSRafael J. Wysocki 1054f721889fSRafael J. Wysocki dle->dev = dev; 1055f721889fSRafael J. Wysocki dle->need_restore = false; 1056f721889fSRafael J. Wysocki list_add_tail(&dle->node, &genpd->dev_list); 1057596ba34bSRafael J. Wysocki genpd->device_count++; 1058f721889fSRafael J. Wysocki 1059f721889fSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1060f721889fSRafael J. Wysocki dev->pm_domain = &genpd->domain; 1061f721889fSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1062f721889fSRafael J. Wysocki 1063f721889fSRafael J. Wysocki out: 106417b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1065f721889fSRafael J. Wysocki 1066f721889fSRafael J. Wysocki return ret; 1067f721889fSRafael J. Wysocki } 1068f721889fSRafael J. Wysocki 1069f721889fSRafael J. Wysocki /** 1070f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1071f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1072f721889fSRafael J. Wysocki * @dev: Device to be removed. 1073f721889fSRafael J. Wysocki */ 1074f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1075f721889fSRafael J. Wysocki struct device *dev) 1076f721889fSRafael J. Wysocki { 1077f721889fSRafael J. Wysocki struct dev_list_entry *dle; 1078f721889fSRafael J. Wysocki int ret = -EINVAL; 1079f721889fSRafael J. Wysocki 1080f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1081f721889fSRafael J. Wysocki 1082f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1083f721889fSRafael J. Wysocki return -EINVAL; 1084f721889fSRafael J. Wysocki 108517b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1086f721889fSRafael J. Wysocki 1087596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1088596ba34bSRafael J. Wysocki ret = -EAGAIN; 1089596ba34bSRafael J. Wysocki goto out; 1090596ba34bSRafael J. Wysocki } 1091596ba34bSRafael J. Wysocki 1092f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) { 1093f721889fSRafael J. Wysocki if (dle->dev != dev) 1094f721889fSRafael J. Wysocki continue; 1095f721889fSRafael J. Wysocki 1096f721889fSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1097f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1098f721889fSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1099f721889fSRafael J. Wysocki 1100596ba34bSRafael J. Wysocki genpd->device_count--; 1101f721889fSRafael J. Wysocki list_del(&dle->node); 1102f721889fSRafael J. Wysocki kfree(dle); 1103f721889fSRafael J. Wysocki 1104f721889fSRafael J. Wysocki ret = 0; 1105f721889fSRafael J. Wysocki break; 1106f721889fSRafael J. Wysocki } 1107f721889fSRafael J. Wysocki 1108596ba34bSRafael J. Wysocki out: 110917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1110f721889fSRafael J. Wysocki 1111f721889fSRafael J. Wysocki return ret; 1112f721889fSRafael J. Wysocki } 1113f721889fSRafael J. Wysocki 1114f721889fSRafael J. Wysocki /** 1115f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1116f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1117f721889fSRafael J. Wysocki * @new_subdomain: Subdomain to be added. 1118f721889fSRafael J. Wysocki */ 1119f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1120f721889fSRafael J. Wysocki struct generic_pm_domain *new_subdomain) 1121f721889fSRafael J. Wysocki { 1122f721889fSRafael J. Wysocki struct generic_pm_domain *subdomain; 1123f721889fSRafael J. Wysocki int ret = 0; 1124f721889fSRafael J. Wysocki 1125f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain)) 1126f721889fSRafael J. Wysocki return -EINVAL; 1127f721889fSRafael J. Wysocki 112817b75ecaSRafael J. Wysocki start: 112917b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 113017b75ecaSRafael J. Wysocki mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING); 1131f721889fSRafael J. Wysocki 113217b75ecaSRafael J. Wysocki if (new_subdomain->status != GPD_STATE_POWER_OFF 113317b75ecaSRafael J. Wysocki && new_subdomain->status != GPD_STATE_ACTIVE) { 113417b75ecaSRafael J. Wysocki mutex_unlock(&new_subdomain->lock); 113517b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 113617b75ecaSRafael J. Wysocki goto start; 113717b75ecaSRafael J. Wysocki } 113817b75ecaSRafael J. Wysocki 113917b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 114017b75ecaSRafael J. Wysocki && new_subdomain->status != GPD_STATE_POWER_OFF) { 1141f721889fSRafael J. Wysocki ret = -EINVAL; 1142f721889fSRafael J. Wysocki goto out; 1143f721889fSRafael J. Wysocki } 1144f721889fSRafael J. Wysocki 1145f721889fSRafael J. Wysocki list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 1146f721889fSRafael J. Wysocki if (subdomain == new_subdomain) { 1147f721889fSRafael J. Wysocki ret = -EINVAL; 1148f721889fSRafael J. Wysocki goto out; 1149f721889fSRafael J. Wysocki } 1150f721889fSRafael J. Wysocki } 1151f721889fSRafael J. Wysocki 1152f721889fSRafael J. Wysocki list_add_tail(&new_subdomain->sd_node, &genpd->sd_list); 1153f721889fSRafael J. Wysocki new_subdomain->parent = genpd; 115417b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1155f721889fSRafael J. Wysocki genpd->sd_count++; 1156f721889fSRafael J. Wysocki 1157f721889fSRafael J. Wysocki out: 115817b75ecaSRafael J. Wysocki mutex_unlock(&new_subdomain->lock); 115917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1160f721889fSRafael J. Wysocki 1161f721889fSRafael J. Wysocki return ret; 1162f721889fSRafael J. Wysocki } 1163f721889fSRafael J. Wysocki 1164f721889fSRafael J. Wysocki /** 1165f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1166f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 1167f721889fSRafael J. Wysocki * @target: Subdomain to be removed. 1168f721889fSRafael J. Wysocki */ 1169f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 1170f721889fSRafael J. Wysocki struct generic_pm_domain *target) 1171f721889fSRafael J. Wysocki { 1172f721889fSRafael J. Wysocki struct generic_pm_domain *subdomain; 1173f721889fSRafael J. Wysocki int ret = -EINVAL; 1174f721889fSRafael J. Wysocki 1175f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target)) 1176f721889fSRafael J. Wysocki return -EINVAL; 1177f721889fSRafael J. Wysocki 117817b75ecaSRafael J. Wysocki start: 117917b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1180f721889fSRafael J. Wysocki 1181f721889fSRafael J. Wysocki list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 1182f721889fSRafael J. Wysocki if (subdomain != target) 1183f721889fSRafael J. Wysocki continue; 1184f721889fSRafael J. Wysocki 1185f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1186f721889fSRafael J. Wysocki 118717b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 118817b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 118917b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 119017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 119117b75ecaSRafael J. Wysocki goto start; 119217b75ecaSRafael J. Wysocki } 119317b75ecaSRafael J. Wysocki 1194f721889fSRafael J. Wysocki list_del(&subdomain->sd_node); 1195f721889fSRafael J. Wysocki subdomain->parent = NULL; 119617b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1197f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1198f721889fSRafael J. Wysocki 1199f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1200f721889fSRafael J. Wysocki 1201f721889fSRafael J. Wysocki ret = 0; 1202f721889fSRafael J. Wysocki break; 1203f721889fSRafael J. Wysocki } 1204f721889fSRafael J. Wysocki 120517b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1206f721889fSRafael J. Wysocki 1207f721889fSRafael J. Wysocki return ret; 1208f721889fSRafael J. Wysocki } 1209f721889fSRafael J. Wysocki 1210f721889fSRafael J. Wysocki /** 1211f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1212f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1213f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1214f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1215f721889fSRafael J. Wysocki */ 1216f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1217f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1218f721889fSRafael J. Wysocki { 1219f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1220f721889fSRafael J. Wysocki return; 1221f721889fSRafael J. Wysocki 1222f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->sd_node); 1223f721889fSRafael J. Wysocki genpd->parent = NULL; 1224f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1225f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->sd_list); 1226f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1227f721889fSRafael J. Wysocki genpd->gov = gov; 1228f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1229f721889fSRafael J. Wysocki genpd->in_progress = 0; 1230f721889fSRafael J. Wysocki genpd->sd_count = 0; 123117b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 123217b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 1233c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 1234c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 1235596ba34bSRafael J. Wysocki genpd->device_count = 0; 1236596ba34bSRafael J. Wysocki genpd->suspended_count = 0; 1237f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1238f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1239f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1240596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1241596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 1242596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1243596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1244596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1245596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 1246596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1247596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1248596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1249596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_dev_poweroff; 1250596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq; 1251596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1252596ba34bSRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_restore; 1253596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 12545125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 12555125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 12565125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 12575125bbf3SRafael J. Wysocki } 12585125bbf3SRafael J. Wysocki 12595125bbf3SRafael J. Wysocki /** 12605125bbf3SRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 12615125bbf3SRafael J. Wysocki */ 12625125bbf3SRafael J. Wysocki void pm_genpd_poweroff_unused(void) 12635125bbf3SRafael J. Wysocki { 12645125bbf3SRafael J. Wysocki struct generic_pm_domain *genpd; 12655125bbf3SRafael J. Wysocki 12665125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 12675125bbf3SRafael J. Wysocki 12685125bbf3SRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 12695125bbf3SRafael J. Wysocki genpd_queue_power_off_work(genpd); 12705125bbf3SRafael J. Wysocki 12715125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 1272f721889fSRafael J. Wysocki } 1273