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 32c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 33f721889fSRafael J. Wysocki { 34c4bb3160SRafael J. Wysocki bool ret = false; 35c4bb3160SRafael J. Wysocki 36c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 37c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 38c4bb3160SRafael J. Wysocki 39c4bb3160SRafael J. Wysocki return ret; 40c4bb3160SRafael J. Wysocki } 41c4bb3160SRafael J. Wysocki 42c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 43c4bb3160SRafael J. Wysocki { 44c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 45c4bb3160SRafael J. Wysocki smp_mb__after_atomic_inc(); 46f721889fSRafael J. Wysocki } 47f721889fSRafael J. Wysocki 4817b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd) 4917b75ecaSRafael J. Wysocki { 5017b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 5117b75ecaSRafael J. Wysocki 5217b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 5317b75ecaSRafael J. Wysocki /* 5417b75ecaSRafael J. Wysocki * Wait for the domain to transition into either the active, 5517b75ecaSRafael J. Wysocki * or the power off state. 5617b75ecaSRafael J. Wysocki */ 5717b75ecaSRafael J. Wysocki for (;;) { 5817b75ecaSRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 5917b75ecaSRafael J. Wysocki TASK_UNINTERRUPTIBLE); 60c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 61c6d22b37SRafael J. Wysocki || genpd->status == GPD_STATE_POWER_OFF) 6217b75ecaSRafael J. Wysocki break; 6317b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 6417b75ecaSRafael J. Wysocki 6517b75ecaSRafael J. Wysocki schedule(); 6617b75ecaSRafael J. Wysocki 6717b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 6817b75ecaSRafael J. Wysocki } 6917b75ecaSRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 7017b75ecaSRafael J. Wysocki } 7117b75ecaSRafael J. Wysocki 7217b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd) 7317b75ecaSRafael J. Wysocki { 7417b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 7517b75ecaSRafael J. Wysocki } 7617b75ecaSRafael J. Wysocki 77c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd) 78c6d22b37SRafael J. Wysocki { 79c6d22b37SRafael J. Wysocki if (genpd->resume_count == 0) 80c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 81c6d22b37SRafael J. Wysocki } 82c6d22b37SRafael J. Wysocki 83f721889fSRafael J. Wysocki /** 845063ce15SRafael J. Wysocki * __pm_genpd_poweron - Restore power to a given PM domain and its masters. 855248051bSRafael J. Wysocki * @genpd: PM domain to power up. 865248051bSRafael J. Wysocki * 875063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 885248051bSRafael J. Wysocki * resume a device belonging to it. 895248051bSRafael J. Wysocki */ 903f241775SRafael J. Wysocki int __pm_genpd_poweron(struct generic_pm_domain *genpd) 913f241775SRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 925248051bSRafael J. Wysocki { 935063ce15SRafael J. Wysocki struct gpd_link *link; 943f241775SRafael J. Wysocki DEFINE_WAIT(wait); 955248051bSRafael J. Wysocki int ret = 0; 965248051bSRafael J. Wysocki 975063ce15SRafael J. Wysocki /* If the domain's master is being waited for, we have to wait too. */ 983f241775SRafael J. Wysocki for (;;) { 993f241775SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 1003f241775SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 10117877eb5SRafael J. Wysocki if (genpd->status != GPD_STATE_WAIT_MASTER) 1023f241775SRafael J. Wysocki break; 1033f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 1043f241775SRafael J. Wysocki 1053f241775SRafael J. Wysocki schedule(); 1063f241775SRafael J. Wysocki 10717b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 1083f241775SRafael J. Wysocki } 1093f241775SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 11017b75ecaSRafael J. Wysocki 11117b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 112596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1133f241775SRafael J. Wysocki return 0; 1145248051bSRafael J. Wysocki 115c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 116c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 1173f241775SRafael J. Wysocki return 0; 118c6d22b37SRafael J. Wysocki } 119c6d22b37SRafael J. Wysocki 1205063ce15SRafael J. Wysocki /* 1215063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 1225063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 1235063ce15SRafael J. Wysocki * with it. 1245063ce15SRafael J. Wysocki */ 1255063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1265063ce15SRafael J. Wysocki genpd_sd_counter_inc(link->master); 12717877eb5SRafael J. Wysocki genpd->status = GPD_STATE_WAIT_MASTER; 1283c07cbc4SRafael J. Wysocki 1295248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1305248051bSRafael J. Wysocki 1315063ce15SRafael J. Wysocki ret = pm_genpd_poweron(link->master); 1329e08cf42SRafael J. Wysocki 1339e08cf42SRafael J. Wysocki mutex_lock(&genpd->lock); 1349e08cf42SRafael J. Wysocki 1353f241775SRafael J. Wysocki /* 1363f241775SRafael J. Wysocki * The "wait for parent" status is guaranteed not to change 1375063ce15SRafael J. Wysocki * while the master is powering on. 1383f241775SRafael J. Wysocki */ 1393f241775SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1403f241775SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 1415063ce15SRafael J. Wysocki if (ret) { 1425063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 1439e08cf42SRafael J. Wysocki goto err; 1445248051bSRafael J. Wysocki } 1455063ce15SRafael J. Wysocki } 1465248051bSRafael J. Wysocki 1479e08cf42SRafael J. Wysocki if (genpd->power_on) { 148fe202fdeSRafael J. Wysocki ret = genpd->power_on(genpd); 1499e08cf42SRafael J. Wysocki if (ret) 1509e08cf42SRafael J. Wysocki goto err; 1513c07cbc4SRafael J. Wysocki } 1525248051bSRafael J. Wysocki 1539e08cf42SRafael J. Wysocki genpd_set_active(genpd); 1549e08cf42SRafael J. Wysocki 1553f241775SRafael J. Wysocki return 0; 1569e08cf42SRafael J. Wysocki 1579e08cf42SRafael J. Wysocki err: 1585063ce15SRafael J. Wysocki list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) 1595063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 1609e08cf42SRafael J. Wysocki 1613f241775SRafael J. Wysocki return ret; 1623f241775SRafael J. Wysocki } 1633f241775SRafael J. Wysocki 1643f241775SRafael J. Wysocki /** 1655063ce15SRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its masters. 1663f241775SRafael J. Wysocki * @genpd: PM domain to power up. 1673f241775SRafael J. Wysocki */ 1683f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd) 1693f241775SRafael J. Wysocki { 1703f241775SRafael J. Wysocki int ret; 1713f241775SRafael J. Wysocki 1723f241775SRafael J. Wysocki mutex_lock(&genpd->lock); 1733f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 1743f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 1753f241775SRafael J. Wysocki return ret; 1765248051bSRafael J. Wysocki } 1775248051bSRafael J. Wysocki 1785248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 1795248051bSRafael J. Wysocki 1805248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 1815248051bSRafael J. Wysocki 1825248051bSRafael J. Wysocki /** 183f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 1844605ab65SRafael J. Wysocki * @pdd: Domain data of the device to save the state of. 185f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 186f721889fSRafael J. Wysocki */ 1874605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd, 188f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 18917b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 190f721889fSRafael J. Wysocki { 191cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1924605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 193f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 194f721889fSRafael J. Wysocki int ret = 0; 195f721889fSRafael J. Wysocki 196cd0ea672SRafael J. Wysocki if (gpd_data->need_restore) 197f721889fSRafael J. Wysocki return 0; 198f721889fSRafael J. Wysocki 19917b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 20017b75ecaSRafael J. Wysocki 201f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_suspend) { 202f721889fSRafael J. Wysocki if (genpd->start_device) 203f721889fSRafael J. Wysocki genpd->start_device(dev); 204f721889fSRafael J. Wysocki 205f721889fSRafael J. Wysocki ret = drv->pm->runtime_suspend(dev); 206f721889fSRafael J. Wysocki 207f721889fSRafael J. Wysocki if (genpd->stop_device) 208f721889fSRafael J. Wysocki genpd->stop_device(dev); 209f721889fSRafael J. Wysocki } 210f721889fSRafael J. Wysocki 21117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 21217b75ecaSRafael J. Wysocki 213f721889fSRafael J. Wysocki if (!ret) 214cd0ea672SRafael J. Wysocki gpd_data->need_restore = true; 215f721889fSRafael J. Wysocki 216f721889fSRafael J. Wysocki return ret; 217f721889fSRafael J. Wysocki } 218f721889fSRafael J. Wysocki 219f721889fSRafael J. Wysocki /** 220f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 2214605ab65SRafael J. Wysocki * @pdd: Domain data of the device to restore the state of. 222f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 223f721889fSRafael J. Wysocki */ 2244605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd, 225f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 22617b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 227f721889fSRafael J. Wysocki { 228cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 2294605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 230f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 231f721889fSRafael J. Wysocki 232cd0ea672SRafael J. Wysocki if (!gpd_data->need_restore) 233f721889fSRafael J. Wysocki return; 234f721889fSRafael J. Wysocki 23517b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 23617b75ecaSRafael J. Wysocki 237f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_resume) { 238f721889fSRafael J. Wysocki if (genpd->start_device) 239f721889fSRafael J. Wysocki genpd->start_device(dev); 240f721889fSRafael J. Wysocki 241f721889fSRafael J. Wysocki drv->pm->runtime_resume(dev); 242f721889fSRafael J. Wysocki 243f721889fSRafael J. Wysocki if (genpd->stop_device) 244f721889fSRafael J. Wysocki genpd->stop_device(dev); 245f721889fSRafael J. Wysocki } 246f721889fSRafael J. Wysocki 24717b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 24817b75ecaSRafael J. Wysocki 249cd0ea672SRafael J. Wysocki gpd_data->need_restore = false; 250f721889fSRafael J. Wysocki } 251f721889fSRafael J. Wysocki 252f721889fSRafael J. Wysocki /** 253c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 254c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 255c6d22b37SRafael J. Wysocki * 256c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 257c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 258c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 259c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 260c6d22b37SRafael J. Wysocki */ 261c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 262c6d22b37SRafael J. Wysocki { 26317877eb5SRafael J. Wysocki return genpd->status == GPD_STATE_WAIT_MASTER 2643f241775SRafael J. Wysocki || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 265c6d22b37SRafael J. Wysocki } 266c6d22b37SRafael J. Wysocki 267c6d22b37SRafael J. Wysocki /** 26856375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 26956375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 27056375fd4SRafael J. Wysocki * 27156375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 27256375fd4SRafael J. Wysocki * before. 27356375fd4SRafael J. Wysocki */ 2740bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 27556375fd4SRafael J. Wysocki { 27656375fd4SRafael J. Wysocki if (!work_pending(&genpd->power_off_work)) 27756375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 27856375fd4SRafael J. Wysocki } 27956375fd4SRafael J. Wysocki 28056375fd4SRafael J. Wysocki /** 281f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 282f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 283f721889fSRafael J. Wysocki * 284f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 285f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 286f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 287f721889fSRafael J. Wysocki */ 288f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 28917b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 290f721889fSRafael J. Wysocki { 2914605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 2925063ce15SRafael J. Wysocki struct gpd_link *link; 293f721889fSRafael J. Wysocki unsigned int not_suspended; 294c6d22b37SRafael J. Wysocki int ret = 0; 295f721889fSRafael J. Wysocki 296c6d22b37SRafael J. Wysocki start: 297c6d22b37SRafael J. Wysocki /* 298c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 299c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 3005063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 301c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 3023f241775SRafael J. Wysocki * (4) System suspend is in progress. 303c6d22b37SRafael J. Wysocki */ 3043f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 30517877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 3063f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 307f721889fSRafael J. Wysocki return 0; 308f721889fSRafael J. Wysocki 309c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 310f721889fSRafael J. Wysocki return -EBUSY; 311f721889fSRafael J. Wysocki 312f721889fSRafael J. Wysocki not_suspended = 0; 3134605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 3140aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 3150aa2a221SRafael J. Wysocki || pdd->dev->power.irq_safe)) 316f721889fSRafael J. Wysocki not_suspended++; 317f721889fSRafael J. Wysocki 318f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 319f721889fSRafael J. Wysocki return -EBUSY; 320f721889fSRafael J. Wysocki 321c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 322c6d22b37SRafael J. Wysocki /* 323c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 324c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 325c6d22b37SRafael J. Wysocki */ 326c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 327c6d22b37SRafael J. Wysocki return 0; 328c6d22b37SRafael J. Wysocki } 329c6d22b37SRafael J. Wysocki 330f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 331f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 332f721889fSRafael J. Wysocki return -EAGAIN; 333f721889fSRafael J. Wysocki } 334f721889fSRafael J. Wysocki 33517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 336c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 33717b75ecaSRafael J. Wysocki 3384605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 3393c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 3404605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 3413f241775SRafael J. Wysocki 3423f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 3433f241775SRafael J. Wysocki goto out; 3443f241775SRafael J. Wysocki 345697a7f37SRafael J. Wysocki if (ret) { 346697a7f37SRafael J. Wysocki genpd_set_active(genpd); 347697a7f37SRafael J. Wysocki goto out; 348697a7f37SRafael J. Wysocki } 349f721889fSRafael J. Wysocki 350c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 351c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 352c6d22b37SRafael J. Wysocki goto start; 353c6d22b37SRafael J. Wysocki } 354c6d22b37SRafael J. Wysocki } 35517b75ecaSRafael J. Wysocki 3563c07cbc4SRafael J. Wysocki if (genpd->power_off) { 3573c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 3583c07cbc4SRafael J. Wysocki ret = -EBUSY; 359c6d22b37SRafael J. Wysocki goto out; 360c6d22b37SRafael J. Wysocki } 36117b75ecaSRafael J. Wysocki 3623c07cbc4SRafael J. Wysocki /* 3635063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 3645063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 3653c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 3663c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 3673c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 3683c07cbc4SRafael J. Wysocki * happen very often). 3693c07cbc4SRafael J. Wysocki */ 370d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 371d2805402SRafael J. Wysocki if (ret == -EBUSY) { 372d2805402SRafael J. Wysocki genpd_set_active(genpd); 373d2805402SRafael J. Wysocki goto out; 374d2805402SRafael J. Wysocki } 375d2805402SRafael J. Wysocki } 376f721889fSRafael J. Wysocki 37717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 378f721889fSRafael J. Wysocki 3795063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 3805063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 3815063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 3825063ce15SRafael J. Wysocki } 38317b75ecaSRafael J. Wysocki 384c6d22b37SRafael J. Wysocki out: 385c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 386c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 387c6d22b37SRafael J. Wysocki return ret; 388f721889fSRafael J. Wysocki } 389f721889fSRafael J. Wysocki 390f721889fSRafael J. Wysocki /** 391f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 392f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 393f721889fSRafael J. Wysocki */ 394f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 395f721889fSRafael J. Wysocki { 396f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 397f721889fSRafael J. Wysocki 398f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 399f721889fSRafael J. Wysocki 40017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 401f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 40217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 403f721889fSRafael J. Wysocki } 404f721889fSRafael J. Wysocki 405f721889fSRafael J. Wysocki /** 406f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 407f721889fSRafael J. Wysocki * @dev: Device to suspend. 408f721889fSRafael J. Wysocki * 409f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 410f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 411f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 412f721889fSRafael J. Wysocki */ 413f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 414f721889fSRafael J. Wysocki { 415f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 416f721889fSRafael J. Wysocki 417f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 418f721889fSRafael J. Wysocki 4195248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4205248051bSRafael J. Wysocki if (IS_ERR(genpd)) 421f721889fSRafael J. Wysocki return -EINVAL; 422f721889fSRafael J. Wysocki 4230aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 4240aa2a221SRafael J. Wysocki 425f721889fSRafael J. Wysocki if (genpd->stop_device) { 426f721889fSRafael J. Wysocki int ret = genpd->stop_device(dev); 427f721889fSRafael J. Wysocki if (ret) 42817b75ecaSRafael J. Wysocki return ret; 429f721889fSRafael J. Wysocki } 43017b75ecaSRafael J. Wysocki 4310aa2a221SRafael J. Wysocki /* 4320aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 4330aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 4340aa2a221SRafael J. Wysocki */ 4350aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 4360aa2a221SRafael J. Wysocki return 0; 4370aa2a221SRafael J. Wysocki 438c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 439f721889fSRafael J. Wysocki genpd->in_progress++; 440f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 441f721889fSRafael J. Wysocki genpd->in_progress--; 442c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 443f721889fSRafael J. Wysocki 444f721889fSRafael J. Wysocki return 0; 445f721889fSRafael J. Wysocki } 446f721889fSRafael J. Wysocki 447f721889fSRafael J. Wysocki /** 448f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 449f721889fSRafael J. Wysocki * @dev: Device to resume. 450f721889fSRafael J. Wysocki * 451f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 452f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 453f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 454f721889fSRafael J. Wysocki */ 455f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 456f721889fSRafael J. Wysocki { 457f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 458c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 459f721889fSRafael J. Wysocki int ret; 460f721889fSRafael J. Wysocki 461f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 462f721889fSRafael J. Wysocki 4635248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4645248051bSRafael J. Wysocki if (IS_ERR(genpd)) 465f721889fSRafael J. Wysocki return -EINVAL; 466f721889fSRafael J. Wysocki 4670aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 4680aa2a221SRafael J. Wysocki 4690aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 4700aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 4710aa2a221SRafael J. Wysocki goto out; 4720aa2a221SRafael J. Wysocki 473c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 4743f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 4753f241775SRafael J. Wysocki if (ret) { 4763f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 4773f241775SRafael J. Wysocki return ret; 4783f241775SRafael J. Wysocki } 47917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 480c6d22b37SRafael J. Wysocki genpd->resume_count++; 481c6d22b37SRafael J. Wysocki for (;;) { 482c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 483c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 484c6d22b37SRafael J. Wysocki /* 485c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 486c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 487c6d22b37SRafael J. Wysocki * not wait. 488c6d22b37SRafael J. Wysocki */ 489c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 490c6d22b37SRafael J. Wysocki break; 491c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 492c6d22b37SRafael J. Wysocki 493c6d22b37SRafael J. Wysocki schedule(); 494c6d22b37SRafael J. Wysocki 495c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 496c6d22b37SRafael J. Wysocki } 497c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 498cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 499c6d22b37SRafael J. Wysocki genpd->resume_count--; 500c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 50117b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 502c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 50317b75ecaSRafael J. Wysocki 5040aa2a221SRafael J. Wysocki out: 50517b75ecaSRafael J. Wysocki if (genpd->start_device) 50617b75ecaSRafael J. Wysocki genpd->start_device(dev); 507f721889fSRafael J. Wysocki 508f721889fSRafael J. Wysocki return 0; 509f721889fSRafael J. Wysocki } 510f721889fSRafael J. Wysocki 51117f2ae7fSRafael J. Wysocki /** 51217f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 51317f2ae7fSRafael J. Wysocki */ 51417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 51517f2ae7fSRafael J. Wysocki { 51617f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 51717f2ae7fSRafael J. Wysocki 51817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 51917f2ae7fSRafael J. Wysocki 52017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 52117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 52217f2ae7fSRafael J. Wysocki 52317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 52417f2ae7fSRafael J. Wysocki } 52517f2ae7fSRafael J. Wysocki 526f721889fSRafael J. Wysocki #else 527f721889fSRafael J. Wysocki 528f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 529f721889fSRafael J. Wysocki 530f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 531f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 532f721889fSRafael J. Wysocki 533f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 534f721889fSRafael J. Wysocki 535596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 536596ba34bSRafael J. Wysocki 537596ba34bSRafael J. Wysocki /** 5385063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 539596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 540596ba34bSRafael J. Wysocki * 541596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 5425063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 543596ba34bSRafael J. Wysocki * 544596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 545596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 546596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 547596ba34bSRafael J. Wysocki */ 548596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 549596ba34bSRafael J. Wysocki { 5505063ce15SRafael J. Wysocki struct gpd_link *link; 551596ba34bSRafael J. Wysocki 55217b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 553596ba34bSRafael J. Wysocki return; 554596ba34bSRafael J. Wysocki 555c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 556c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 557596ba34bSRafael J. Wysocki return; 558596ba34bSRafael J. Wysocki 559596ba34bSRafael J. Wysocki if (genpd->power_off) 560596ba34bSRafael J. Wysocki genpd->power_off(genpd); 561596ba34bSRafael J. Wysocki 56217b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 5635063ce15SRafael J. Wysocki 5645063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5655063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 5665063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 567596ba34bSRafael J. Wysocki } 568596ba34bSRafael J. Wysocki } 569596ba34bSRafael J. Wysocki 570596ba34bSRafael J. Wysocki /** 5714ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 5724ecd6e65SRafael J. Wysocki * @dev: Device to check. 5734ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 5744ecd6e65SRafael J. Wysocki * 5754ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 5764ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 5774ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 5784ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 5794ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 5804ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 5814ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 5824ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 5834ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 5844ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 5854ecd6e65SRafael J. Wysocki */ 5864ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 5874ecd6e65SRafael J. Wysocki { 5884ecd6e65SRafael J. Wysocki bool active_wakeup; 5894ecd6e65SRafael J. Wysocki 5904ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 5914ecd6e65SRafael J. Wysocki return false; 5924ecd6e65SRafael J. Wysocki 5934ecd6e65SRafael J. Wysocki active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev); 5944ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 5954ecd6e65SRafael J. Wysocki } 5964ecd6e65SRafael J. Wysocki 5974ecd6e65SRafael J. Wysocki /** 598596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 599596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 600596ba34bSRafael J. Wysocki * 601596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 602596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 603596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 604596ba34bSRafael J. Wysocki * consisting of I/O devices. 605596ba34bSRafael J. Wysocki */ 606596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 607596ba34bSRafael J. Wysocki { 608596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 609b6c10c84SRafael J. Wysocki int ret; 610596ba34bSRafael J. Wysocki 611596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 612596ba34bSRafael J. Wysocki 613596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 614596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 615596ba34bSRafael J. Wysocki return -EINVAL; 616596ba34bSRafael J. Wysocki 61717b75ecaSRafael J. Wysocki /* 61817b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 61917b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 62017b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 62117b75ecaSRafael J. Wysocki */ 62217b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 62317b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 62417b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 62517b75ecaSRafael J. Wysocki 62617b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 62717b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 62817b75ecaSRafael J. Wysocki return -EBUSY; 62917b75ecaSRafael J. Wysocki } 63017b75ecaSRafael J. Wysocki 6314ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 6324ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 6334ecd6e65SRafael J. Wysocki 63417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 635596ba34bSRafael J. Wysocki 636596ba34bSRafael J. Wysocki if (genpd->prepared_count++ == 0) 63717b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 63817b75ecaSRafael J. Wysocki 63917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 640596ba34bSRafael J. Wysocki 641596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 64217b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 643596ba34bSRafael J. Wysocki return 0; 644596ba34bSRafael J. Wysocki } 645596ba34bSRafael J. Wysocki 646596ba34bSRafael J. Wysocki /* 64717b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 64817b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 64917b75ecaSRafael J. Wysocki * is suspended (e.g. it's been stopped by .stop_device()), we need 65017b75ecaSRafael J. Wysocki * to make it operational. 651596ba34bSRafael J. Wysocki */ 65217b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 653596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 654596ba34bSRafael J. Wysocki 655b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 656b6c10c84SRafael J. Wysocki if (ret) { 657b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 658b6c10c84SRafael J. Wysocki 659b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 660b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 661b6c10c84SRafael J. Wysocki 662b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 66317b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 664b6c10c84SRafael J. Wysocki } 66517b75ecaSRafael J. Wysocki 66617b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 667b6c10c84SRafael J. Wysocki return ret; 668596ba34bSRafael J. Wysocki } 669596ba34bSRafael J. Wysocki 670596ba34bSRafael J. Wysocki /** 671596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 672596ba34bSRafael J. Wysocki * @dev: Device to suspend. 673596ba34bSRafael J. Wysocki * 674596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 675596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 676596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 677596ba34bSRafael J. Wysocki */ 678596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 679596ba34bSRafael J. Wysocki { 680596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 681596ba34bSRafael J. Wysocki 682596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 683596ba34bSRafael J. Wysocki 684596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 685596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 686596ba34bSRafael J. Wysocki return -EINVAL; 687596ba34bSRafael J. Wysocki 688596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 689596ba34bSRafael J. Wysocki } 690596ba34bSRafael J. Wysocki 691596ba34bSRafael J. Wysocki /** 692596ba34bSRafael J. Wysocki * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain. 693596ba34bSRafael J. Wysocki * @dev: Device to suspend. 694596ba34bSRafael J. Wysocki * 695596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 696596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 697596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 698596ba34bSRafael J. Wysocki */ 699596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 700596ba34bSRafael J. Wysocki { 701596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 702596ba34bSRafael J. Wysocki int ret; 703596ba34bSRafael J. Wysocki 704596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 705596ba34bSRafael J. Wysocki 706596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 707596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 708596ba34bSRafael J. Wysocki return -EINVAL; 709596ba34bSRafael J. Wysocki 710596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 711596ba34bSRafael J. Wysocki return 0; 712596ba34bSRafael J. Wysocki 713596ba34bSRafael J. Wysocki ret = pm_generic_suspend_noirq(dev); 714596ba34bSRafael J. Wysocki if (ret) 715596ba34bSRafael J. Wysocki return ret; 716596ba34bSRafael J. Wysocki 717d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 718d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 719d4f2d87aSRafael J. Wysocki return 0; 720d4f2d87aSRafael J. Wysocki 721596ba34bSRafael J. Wysocki if (genpd->stop_device) 722596ba34bSRafael J. Wysocki genpd->stop_device(dev); 723596ba34bSRafael J. Wysocki 724596ba34bSRafael J. Wysocki /* 725596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 726596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 727596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 728596ba34bSRafael J. Wysocki */ 729596ba34bSRafael J. Wysocki genpd->suspended_count++; 730596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 731596ba34bSRafael J. Wysocki 732596ba34bSRafael J. Wysocki return 0; 733596ba34bSRafael J. Wysocki } 734596ba34bSRafael J. Wysocki 735596ba34bSRafael J. Wysocki /** 736596ba34bSRafael J. Wysocki * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain. 737596ba34bSRafael J. Wysocki * @dev: Device to resume. 738596ba34bSRafael J. Wysocki * 739596ba34bSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 740596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 741596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 742596ba34bSRafael J. Wysocki * devices. 743596ba34bSRafael J. Wysocki */ 744596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 745596ba34bSRafael J. Wysocki { 746596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 747596ba34bSRafael J. Wysocki 748596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 749596ba34bSRafael J. Wysocki 750596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 751596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 752596ba34bSRafael J. Wysocki return -EINVAL; 753596ba34bSRafael J. Wysocki 754596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 755596ba34bSRafael J. Wysocki return 0; 756596ba34bSRafael J. Wysocki 757596ba34bSRafael J. Wysocki /* 758596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 759596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 760596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 761596ba34bSRafael J. Wysocki */ 762596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 763596ba34bSRafael J. Wysocki genpd->suspended_count--; 764596ba34bSRafael J. Wysocki if (genpd->start_device) 765596ba34bSRafael J. Wysocki genpd->start_device(dev); 766596ba34bSRafael J. Wysocki 767596ba34bSRafael J. Wysocki return pm_generic_resume_noirq(dev); 768596ba34bSRafael J. Wysocki } 769596ba34bSRafael J. Wysocki 770596ba34bSRafael J. Wysocki /** 771596ba34bSRafael J. Wysocki * pm_genpd_resume - Resume a device belonging to an I/O power domain. 772596ba34bSRafael J. Wysocki * @dev: Device to resume. 773596ba34bSRafael J. Wysocki * 774596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 775596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 776596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 777596ba34bSRafael J. Wysocki */ 778596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 779596ba34bSRafael J. Wysocki { 780596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 781596ba34bSRafael J. Wysocki 782596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 783596ba34bSRafael J. Wysocki 784596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 785596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 786596ba34bSRafael J. Wysocki return -EINVAL; 787596ba34bSRafael J. Wysocki 788596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 789596ba34bSRafael J. Wysocki } 790596ba34bSRafael J. Wysocki 791596ba34bSRafael J. Wysocki /** 792596ba34bSRafael J. Wysocki * pm_genpd_freeze - Freeze a device belonging to an I/O power domain. 793596ba34bSRafael J. Wysocki * @dev: Device to freeze. 794596ba34bSRafael J. Wysocki * 795596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 796596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 797596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 798596ba34bSRafael J. Wysocki */ 799596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 800596ba34bSRafael J. Wysocki { 801596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 802596ba34bSRafael J. Wysocki 803596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 804596ba34bSRafael J. Wysocki 805596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 806596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 807596ba34bSRafael J. Wysocki return -EINVAL; 808596ba34bSRafael J. Wysocki 809596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 810596ba34bSRafael J. Wysocki } 811596ba34bSRafael J. Wysocki 812596ba34bSRafael J. Wysocki /** 813596ba34bSRafael J. Wysocki * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain. 814596ba34bSRafael J. Wysocki * @dev: Device to freeze. 815596ba34bSRafael J. Wysocki * 816596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 817596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 818596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 819596ba34bSRafael J. Wysocki * devices. 820596ba34bSRafael J. Wysocki */ 821596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 822596ba34bSRafael J. Wysocki { 823596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 824596ba34bSRafael J. Wysocki int ret; 825596ba34bSRafael J. Wysocki 826596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 827596ba34bSRafael J. Wysocki 828596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 829596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 830596ba34bSRafael J. Wysocki return -EINVAL; 831596ba34bSRafael J. Wysocki 832596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 833596ba34bSRafael J. Wysocki return 0; 834596ba34bSRafael J. Wysocki 835596ba34bSRafael J. Wysocki ret = pm_generic_freeze_noirq(dev); 836596ba34bSRafael J. Wysocki if (ret) 837596ba34bSRafael J. Wysocki return ret; 838596ba34bSRafael J. Wysocki 839596ba34bSRafael J. Wysocki if (genpd->stop_device) 840596ba34bSRafael J. Wysocki genpd->stop_device(dev); 841596ba34bSRafael J. Wysocki 842596ba34bSRafael J. Wysocki return 0; 843596ba34bSRafael J. Wysocki } 844596ba34bSRafael J. Wysocki 845596ba34bSRafael J. Wysocki /** 846596ba34bSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain. 847596ba34bSRafael J. Wysocki * @dev: Device to thaw. 848596ba34bSRafael J. Wysocki * 849596ba34bSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 850596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 851596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 852596ba34bSRafael J. Wysocki * devices. 853596ba34bSRafael J. Wysocki */ 854596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 855596ba34bSRafael J. Wysocki { 856596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 857596ba34bSRafael J. Wysocki 858596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 859596ba34bSRafael J. Wysocki 860596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 861596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 862596ba34bSRafael J. Wysocki return -EINVAL; 863596ba34bSRafael J. Wysocki 864596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 865596ba34bSRafael J. Wysocki return 0; 866596ba34bSRafael J. Wysocki 867596ba34bSRafael J. Wysocki if (genpd->start_device) 868596ba34bSRafael J. Wysocki genpd->start_device(dev); 869596ba34bSRafael J. Wysocki 870596ba34bSRafael J. Wysocki return pm_generic_thaw_noirq(dev); 871596ba34bSRafael J. Wysocki } 872596ba34bSRafael J. Wysocki 873596ba34bSRafael J. Wysocki /** 874596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 875596ba34bSRafael J. Wysocki * @dev: Device to thaw. 876596ba34bSRafael J. Wysocki * 877596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 878596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 879596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 880596ba34bSRafael J. Wysocki */ 881596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 882596ba34bSRafael J. Wysocki { 883596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 884596ba34bSRafael J. Wysocki 885596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 886596ba34bSRafael J. Wysocki 887596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 888596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 889596ba34bSRafael J. Wysocki return -EINVAL; 890596ba34bSRafael J. Wysocki 891596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 892596ba34bSRafael J. Wysocki } 893596ba34bSRafael J. Wysocki 894596ba34bSRafael J. Wysocki /** 895596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain. 896596ba34bSRafael J. Wysocki * @dev: Device to suspend. 897596ba34bSRafael J. Wysocki * 898596ba34bSRafael J. Wysocki * Power off a device under the assumption that its pm_domain field points to 899596ba34bSRafael J. Wysocki * the domain member of an object of type struct generic_pm_domain representing 900596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 901596ba34bSRafael J. Wysocki */ 902596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev) 903596ba34bSRafael J. Wysocki { 904596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 905596ba34bSRafael J. Wysocki 906596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 907596ba34bSRafael J. Wysocki 908596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 909596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 910596ba34bSRafael J. Wysocki return -EINVAL; 911596ba34bSRafael J. Wysocki 912596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev); 913596ba34bSRafael J. Wysocki } 914596ba34bSRafael J. Wysocki 915596ba34bSRafael J. Wysocki /** 916596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain. 917596ba34bSRafael J. Wysocki * @dev: Device to suspend. 918596ba34bSRafael J. Wysocki * 919596ba34bSRafael J. Wysocki * Carry out a late powering off of a device under the assumption that its 920596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 921596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 922596ba34bSRafael J. Wysocki */ 923596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev) 924596ba34bSRafael J. Wysocki { 925596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 926596ba34bSRafael J. Wysocki int ret; 927596ba34bSRafael J. Wysocki 928596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 929596ba34bSRafael J. Wysocki 930596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 931596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 932596ba34bSRafael J. Wysocki return -EINVAL; 933596ba34bSRafael J. Wysocki 934596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 935596ba34bSRafael J. Wysocki return 0; 936596ba34bSRafael J. Wysocki 937596ba34bSRafael J. Wysocki ret = pm_generic_poweroff_noirq(dev); 938596ba34bSRafael J. Wysocki if (ret) 939596ba34bSRafael J. Wysocki return ret; 940596ba34bSRafael J. Wysocki 941d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 942d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 943d4f2d87aSRafael J. Wysocki return 0; 944d4f2d87aSRafael J. Wysocki 945596ba34bSRafael J. Wysocki if (genpd->stop_device) 946596ba34bSRafael J. Wysocki genpd->stop_device(dev); 947596ba34bSRafael J. Wysocki 948596ba34bSRafael J. Wysocki /* 949596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 950596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 951596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 952596ba34bSRafael J. Wysocki */ 953596ba34bSRafael J. Wysocki genpd->suspended_count++; 954596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 955596ba34bSRafael J. Wysocki 956596ba34bSRafael J. Wysocki return 0; 957596ba34bSRafael J. Wysocki } 958596ba34bSRafael J. Wysocki 959596ba34bSRafael J. Wysocki /** 960596ba34bSRafael J. Wysocki * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain. 961596ba34bSRafael J. Wysocki * @dev: Device to resume. 962596ba34bSRafael J. Wysocki * 963596ba34bSRafael J. Wysocki * Carry out an early restore of a device under the assumption that its 964596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 965596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 966596ba34bSRafael J. Wysocki * devices. 967596ba34bSRafael J. Wysocki */ 968596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 969596ba34bSRafael J. Wysocki { 970596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 971596ba34bSRafael J. Wysocki 972596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 973596ba34bSRafael J. Wysocki 974596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 975596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 976596ba34bSRafael J. Wysocki return -EINVAL; 977596ba34bSRafael J. Wysocki 978596ba34bSRafael J. Wysocki /* 979596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 980596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 981596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 982596ba34bSRafael J. Wysocki */ 98317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 984596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 985596ba34bSRafael J. Wysocki /* 986596ba34bSRafael J. Wysocki * The boot kernel might put the domain into the power on state, 987596ba34bSRafael J. Wysocki * so make sure it really is powered off. 988596ba34bSRafael J. Wysocki */ 989596ba34bSRafael J. Wysocki if (genpd->power_off) 990596ba34bSRafael J. Wysocki genpd->power_off(genpd); 991596ba34bSRafael J. Wysocki return 0; 992596ba34bSRafael J. Wysocki } 993596ba34bSRafael J. Wysocki 994596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 995596ba34bSRafael J. Wysocki genpd->suspended_count--; 996596ba34bSRafael J. Wysocki if (genpd->start_device) 997596ba34bSRafael J. Wysocki genpd->start_device(dev); 998596ba34bSRafael J. Wysocki 999596ba34bSRafael J. Wysocki return pm_generic_restore_noirq(dev); 1000596ba34bSRafael J. Wysocki } 1001596ba34bSRafael J. Wysocki 1002596ba34bSRafael J. Wysocki /** 1003596ba34bSRafael J. Wysocki * pm_genpd_restore - Restore a device belonging to an I/O power domain. 1004596ba34bSRafael J. Wysocki * @dev: Device to resume. 1005596ba34bSRafael J. Wysocki * 1006596ba34bSRafael J. Wysocki * Restore a device under the assumption that its pm_domain field points to the 1007596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1008596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1009596ba34bSRafael J. Wysocki */ 1010596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev) 1011596ba34bSRafael J. Wysocki { 1012596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1013596ba34bSRafael J. Wysocki 1014596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1015596ba34bSRafael J. Wysocki 1016596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1017596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1018596ba34bSRafael J. Wysocki return -EINVAL; 1019596ba34bSRafael J. Wysocki 1020596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_restore(dev); 1021596ba34bSRafael J. Wysocki } 1022596ba34bSRafael J. Wysocki 1023596ba34bSRafael J. Wysocki /** 1024596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1025596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1026596ba34bSRafael J. Wysocki * 1027596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1028596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1029596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1030596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1031596ba34bSRafael J. Wysocki */ 1032596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1033596ba34bSRafael J. Wysocki { 1034596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1035596ba34bSRafael J. Wysocki bool run_complete; 1036596ba34bSRafael J. Wysocki 1037596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1038596ba34bSRafael J. Wysocki 1039596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1040596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1041596ba34bSRafael J. Wysocki return; 1042596ba34bSRafael J. Wysocki 1043596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1044596ba34bSRafael J. Wysocki 1045596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1046596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1047596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1048596ba34bSRafael J. Wysocki 1049596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1050596ba34bSRafael J. Wysocki 1051596ba34bSRafael J. Wysocki if (run_complete) { 1052596ba34bSRafael J. Wysocki pm_generic_complete(dev); 10536f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1054596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 10556f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 1056596ba34bSRafael J. Wysocki } 1057596ba34bSRafael J. Wysocki } 1058596ba34bSRafael J. Wysocki 1059596ba34bSRafael J. Wysocki #else 1060596ba34bSRafael J. Wysocki 1061596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1062596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 1063596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 1064596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1065596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1066596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 1067596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 1068596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1069596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1070596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq NULL 1071596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff NULL 1072596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1073596ba34bSRafael J. Wysocki #define pm_genpd_restore NULL 1074596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1075596ba34bSRafael J. Wysocki 1076596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1077596ba34bSRafael J. Wysocki 1078f721889fSRafael J. Wysocki /** 1079f721889fSRafael J. Wysocki * pm_genpd_add_device - Add a device to an I/O PM domain. 1080f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1081f721889fSRafael J. Wysocki * @dev: Device to be added. 1082f721889fSRafael J. Wysocki */ 1083f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 1084f721889fSRafael J. Wysocki { 1085cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 10864605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1087f721889fSRafael J. Wysocki int ret = 0; 1088f721889fSRafael J. Wysocki 1089f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1090f721889fSRafael J. Wysocki 1091f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1092f721889fSRafael J. Wysocki return -EINVAL; 1093f721889fSRafael J. Wysocki 109417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1095f721889fSRafael J. Wysocki 109617b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) { 1097f721889fSRafael J. Wysocki ret = -EINVAL; 1098f721889fSRafael J. Wysocki goto out; 1099f721889fSRafael J. Wysocki } 1100f721889fSRafael J. Wysocki 1101596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1102596ba34bSRafael J. Wysocki ret = -EAGAIN; 1103596ba34bSRafael J. Wysocki goto out; 1104596ba34bSRafael J. Wysocki } 1105596ba34bSRafael J. Wysocki 11064605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 11074605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1108f721889fSRafael J. Wysocki ret = -EINVAL; 1109f721889fSRafael J. Wysocki goto out; 1110f721889fSRafael J. Wysocki } 1111f721889fSRafael J. Wysocki 1112cd0ea672SRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 1113cd0ea672SRafael J. Wysocki if (!gpd_data) { 1114cd0ea672SRafael J. Wysocki ret = -ENOMEM; 1115cd0ea672SRafael J. Wysocki goto out; 1116cd0ea672SRafael J. Wysocki } 1117cd0ea672SRafael J. Wysocki 1118596ba34bSRafael J. Wysocki genpd->device_count++; 1119f721889fSRafael J. Wysocki 1120f721889fSRafael J. Wysocki dev->pm_domain = &genpd->domain; 11214605ab65SRafael J. Wysocki dev_pm_get_subsys_data(dev); 1122cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 1123cd0ea672SRafael J. Wysocki gpd_data->base.dev = dev; 1124cd0ea672SRafael J. Wysocki gpd_data->need_restore = false; 1125cd0ea672SRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 1126f721889fSRafael J. Wysocki 1127f721889fSRafael J. Wysocki out: 112817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1129f721889fSRafael J. Wysocki 1130f721889fSRafael J. Wysocki return ret; 1131f721889fSRafael J. Wysocki } 1132f721889fSRafael J. Wysocki 1133f721889fSRafael J. Wysocki /** 1134f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1135f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1136f721889fSRafael J. Wysocki * @dev: Device to be removed. 1137f721889fSRafael J. Wysocki */ 1138f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1139f721889fSRafael J. Wysocki struct device *dev) 1140f721889fSRafael J. Wysocki { 11414605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1142f721889fSRafael J. Wysocki int ret = -EINVAL; 1143f721889fSRafael J. Wysocki 1144f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1145f721889fSRafael J. Wysocki 1146f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1147f721889fSRafael J. Wysocki return -EINVAL; 1148f721889fSRafael J. Wysocki 114917b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1150f721889fSRafael J. Wysocki 1151596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1152596ba34bSRafael J. Wysocki ret = -EAGAIN; 1153596ba34bSRafael J. Wysocki goto out; 1154596ba34bSRafael J. Wysocki } 1155596ba34bSRafael J. Wysocki 11564605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) { 11574605ab65SRafael J. Wysocki if (pdd->dev != dev) 1158f721889fSRafael J. Wysocki continue; 1159f721889fSRafael J. Wysocki 11604605ab65SRafael J. Wysocki list_del_init(&pdd->list_node); 11614605ab65SRafael J. Wysocki pdd->dev = NULL; 11624605ab65SRafael J. Wysocki dev_pm_put_subsys_data(dev); 1163f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1164cd0ea672SRafael J. Wysocki kfree(to_gpd_data(pdd)); 1165f721889fSRafael J. Wysocki 1166596ba34bSRafael J. Wysocki genpd->device_count--; 1167f721889fSRafael J. Wysocki 1168f721889fSRafael J. Wysocki ret = 0; 1169f721889fSRafael J. Wysocki break; 1170f721889fSRafael J. Wysocki } 1171f721889fSRafael J. Wysocki 1172596ba34bSRafael J. Wysocki out: 117317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1174f721889fSRafael J. Wysocki 1175f721889fSRafael J. Wysocki return ret; 1176f721889fSRafael J. Wysocki } 1177f721889fSRafael J. Wysocki 1178f721889fSRafael J. Wysocki /** 1179f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1180f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1181bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1182f721889fSRafael J. Wysocki */ 1183f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1184bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1185f721889fSRafael J. Wysocki { 11865063ce15SRafael J. Wysocki struct gpd_link *link; 1187f721889fSRafael J. Wysocki int ret = 0; 1188f721889fSRafael J. Wysocki 1189bc0403ffSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1190f721889fSRafael J. Wysocki return -EINVAL; 1191f721889fSRafael J. Wysocki 119217b75ecaSRafael J. Wysocki start: 119317b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1194bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1195f721889fSRafael J. Wysocki 1196bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1197bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1198bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 119917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 120017b75ecaSRafael J. Wysocki goto start; 120117b75ecaSRafael J. Wysocki } 120217b75ecaSRafael J. Wysocki 120317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1204bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1205f721889fSRafael J. Wysocki ret = -EINVAL; 1206f721889fSRafael J. Wysocki goto out; 1207f721889fSRafael J. Wysocki } 1208f721889fSRafael J. Wysocki 12095063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1210bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1211f721889fSRafael J. Wysocki ret = -EINVAL; 1212f721889fSRafael J. Wysocki goto out; 1213f721889fSRafael J. Wysocki } 1214f721889fSRafael J. Wysocki } 1215f721889fSRafael J. Wysocki 12165063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 12175063ce15SRafael J. Wysocki if (!link) { 12185063ce15SRafael J. Wysocki ret = -ENOMEM; 12195063ce15SRafael J. Wysocki goto out; 12205063ce15SRafael J. Wysocki } 12215063ce15SRafael J. Wysocki link->master = genpd; 12225063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1223bc0403ffSRafael J. Wysocki link->slave = subdomain; 1224bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1225bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1226c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1227f721889fSRafael J. Wysocki 1228f721889fSRafael J. Wysocki out: 1229bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 123017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1231f721889fSRafael J. Wysocki 1232f721889fSRafael J. Wysocki return ret; 1233f721889fSRafael J. Wysocki } 1234f721889fSRafael J. Wysocki 1235f721889fSRafael J. Wysocki /** 1236f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1237f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 12385063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1239f721889fSRafael J. Wysocki */ 1240f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 12415063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1242f721889fSRafael J. Wysocki { 12435063ce15SRafael J. Wysocki struct gpd_link *link; 1244f721889fSRafael J. Wysocki int ret = -EINVAL; 1245f721889fSRafael J. Wysocki 12465063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1247f721889fSRafael J. Wysocki return -EINVAL; 1248f721889fSRafael J. Wysocki 124917b75ecaSRafael J. Wysocki start: 125017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1251f721889fSRafael J. Wysocki 12525063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 12535063ce15SRafael J. Wysocki if (link->slave != subdomain) 1254f721889fSRafael J. Wysocki continue; 1255f721889fSRafael J. Wysocki 1256f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1257f721889fSRafael J. Wysocki 125817b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 125917b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 126017b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 126117b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 126217b75ecaSRafael J. Wysocki goto start; 126317b75ecaSRafael J. Wysocki } 126417b75ecaSRafael J. Wysocki 12655063ce15SRafael J. Wysocki list_del(&link->master_node); 12665063ce15SRafael J. Wysocki list_del(&link->slave_node); 12675063ce15SRafael J. Wysocki kfree(link); 126817b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1269f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1270f721889fSRafael J. Wysocki 1271f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1272f721889fSRafael J. Wysocki 1273f721889fSRafael J. Wysocki ret = 0; 1274f721889fSRafael J. Wysocki break; 1275f721889fSRafael J. Wysocki } 1276f721889fSRafael J. Wysocki 127717b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1278f721889fSRafael J. Wysocki 1279f721889fSRafael J. Wysocki return ret; 1280f721889fSRafael J. Wysocki } 1281f721889fSRafael J. Wysocki 1282f721889fSRafael J. Wysocki /** 1283f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1284f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1285f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1286f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1287f721889fSRafael J. Wysocki */ 1288f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1289f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1290f721889fSRafael J. Wysocki { 1291f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1292f721889fSRafael J. Wysocki return; 1293f721889fSRafael J. Wysocki 12945063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 12955063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1296f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1297f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1298f721889fSRafael J. Wysocki genpd->gov = gov; 1299f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1300f721889fSRafael J. Wysocki genpd->in_progress = 0; 1301c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 130217b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 130317b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 1304c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 1305c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 1306596ba34bSRafael J. Wysocki genpd->device_count = 0; 1307596ba34bSRafael J. Wysocki genpd->suspended_count = 0; 1308f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1309f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1310f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1311596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1312596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 1313596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1314596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1315596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1316596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 1317596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1318596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1319596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1320596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_dev_poweroff; 1321596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq; 1322596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1323596ba34bSRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_restore; 1324596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 13255125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 13265125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 13275125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 13285125bbf3SRafael J. Wysocki } 1329