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 195248051bSRafael J. Wysocki #ifdef CONFIG_PM 205248051bSRafael J. Wysocki 215248051bSRafael J. Wysocki static struct generic_pm_domain *dev_to_genpd(struct device *dev) 225248051bSRafael J. Wysocki { 235248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 245248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 255248051bSRafael J. Wysocki 26596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 275248051bSRafael J. Wysocki } 28f721889fSRafael J. Wysocki 29f721889fSRafael J. Wysocki static void genpd_sd_counter_dec(struct generic_pm_domain *genpd) 30f721889fSRafael J. Wysocki { 31f721889fSRafael J. Wysocki if (!WARN_ON(genpd->sd_count == 0)) 32f721889fSRafael J. Wysocki genpd->sd_count--; 33f721889fSRafael J. Wysocki } 34f721889fSRafael J. Wysocki 3517b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd) 3617b75ecaSRafael J. Wysocki { 3717b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 3817b75ecaSRafael J. Wysocki 3917b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 4017b75ecaSRafael J. Wysocki /* 4117b75ecaSRafael J. Wysocki * Wait for the domain to transition into either the active, 4217b75ecaSRafael J. Wysocki * or the power off state. 4317b75ecaSRafael J. Wysocki */ 4417b75ecaSRafael J. Wysocki for (;;) { 4517b75ecaSRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 4617b75ecaSRafael J. Wysocki TASK_UNINTERRUPTIBLE); 47c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 48c6d22b37SRafael J. Wysocki || genpd->status == GPD_STATE_POWER_OFF) 4917b75ecaSRafael J. Wysocki break; 5017b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 5117b75ecaSRafael J. Wysocki 5217b75ecaSRafael J. Wysocki schedule(); 5317b75ecaSRafael J. Wysocki 5417b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 5517b75ecaSRafael J. Wysocki } 5617b75ecaSRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 5717b75ecaSRafael J. Wysocki } 5817b75ecaSRafael J. Wysocki 5917b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd) 6017b75ecaSRafael J. Wysocki { 6117b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 6217b75ecaSRafael J. Wysocki } 6317b75ecaSRafael J. Wysocki 64c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd) 65c6d22b37SRafael J. Wysocki { 66c6d22b37SRafael J. Wysocki if (genpd->resume_count == 0) 67c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 68c6d22b37SRafael J. Wysocki } 69c6d22b37SRafael J. Wysocki 70f721889fSRafael J. Wysocki /** 715248051bSRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its parents. 725248051bSRafael J. Wysocki * @genpd: PM domain to power up. 735248051bSRafael J. Wysocki * 745248051bSRafael J. Wysocki * Restore power to @genpd and all of its parents so that it is possible to 755248051bSRafael J. Wysocki * resume a device belonging to it. 765248051bSRafael J. Wysocki */ 7718b4f3f5SMagnus Damm int pm_genpd_poweron(struct generic_pm_domain *genpd) 785248051bSRafael J. Wysocki { 7917b75ecaSRafael J. Wysocki struct generic_pm_domain *parent = genpd->parent; 8017b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 815248051bSRafael J. Wysocki int ret = 0; 825248051bSRafael J. Wysocki 835248051bSRafael J. Wysocki start: 8417b75ecaSRafael J. Wysocki if (parent) { 85c6d22b37SRafael J. Wysocki genpd_acquire_lock(parent); 865248051bSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 8717b75ecaSRafael J. Wysocki } else { 8817b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 8917b75ecaSRafael J. Wysocki } 9017b75ecaSRafael J. Wysocki 9117b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 92596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 935248051bSRafael J. Wysocki goto out; 945248051bSRafael J. Wysocki 95c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 96c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 97c6d22b37SRafael J. Wysocki goto out; 98c6d22b37SRafael J. Wysocki } 99c6d22b37SRafael J. Wysocki 10017b75ecaSRafael J. Wysocki if (parent && parent->status != GPD_STATE_ACTIVE) { 1015248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 102c6d22b37SRafael J. Wysocki genpd_release_lock(parent); 1035248051bSRafael J. Wysocki 10417b75ecaSRafael J. Wysocki ret = pm_genpd_poweron(parent); 1055248051bSRafael J. Wysocki if (ret) 1065248051bSRafael J. Wysocki return ret; 1075248051bSRafael J. Wysocki 1085248051bSRafael J. Wysocki goto start; 1095248051bSRafael J. Wysocki } 1105248051bSRafael J. Wysocki 1115248051bSRafael J. Wysocki if (genpd->power_on) { 1125248051bSRafael J. Wysocki int ret = genpd->power_on(genpd); 1135248051bSRafael J. Wysocki if (ret) 1145248051bSRafael J. Wysocki goto out; 1155248051bSRafael J. Wysocki } 1165248051bSRafael J. Wysocki 117c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 11817b75ecaSRafael J. Wysocki if (parent) 11917b75ecaSRafael J. Wysocki parent->sd_count++; 1205248051bSRafael J. Wysocki 1215248051bSRafael J. Wysocki out: 1225248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 12317b75ecaSRafael J. Wysocki if (parent) 124c6d22b37SRafael J. Wysocki genpd_release_lock(parent); 1255248051bSRafael J. Wysocki 1265248051bSRafael J. Wysocki return ret; 1275248051bSRafael J. Wysocki } 1285248051bSRafael J. Wysocki 1295248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 1305248051bSRafael J. Wysocki 1315248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 1325248051bSRafael J. Wysocki 1335248051bSRafael J. Wysocki /** 134f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 135f721889fSRafael J. Wysocki * @dle: Device list entry of the device to save the state of. 136f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 137f721889fSRafael J. Wysocki */ 138f721889fSRafael J. Wysocki static int __pm_genpd_save_device(struct dev_list_entry *dle, 139f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 14017b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 141f721889fSRafael J. Wysocki { 142f721889fSRafael J. Wysocki struct device *dev = dle->dev; 143f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 144f721889fSRafael J. Wysocki int ret = 0; 145f721889fSRafael J. Wysocki 146f721889fSRafael J. Wysocki if (dle->need_restore) 147f721889fSRafael J. Wysocki return 0; 148f721889fSRafael J. Wysocki 14917b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 15017b75ecaSRafael J. Wysocki 151f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_suspend) { 152f721889fSRafael J. Wysocki if (genpd->start_device) 153f721889fSRafael J. Wysocki genpd->start_device(dev); 154f721889fSRafael J. Wysocki 155f721889fSRafael J. Wysocki ret = drv->pm->runtime_suspend(dev); 156f721889fSRafael J. Wysocki 157f721889fSRafael J. Wysocki if (genpd->stop_device) 158f721889fSRafael J. Wysocki genpd->stop_device(dev); 159f721889fSRafael J. Wysocki } 160f721889fSRafael J. Wysocki 16117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 16217b75ecaSRafael J. Wysocki 163f721889fSRafael J. Wysocki if (!ret) 164f721889fSRafael J. Wysocki dle->need_restore = true; 165f721889fSRafael J. Wysocki 166f721889fSRafael J. Wysocki return ret; 167f721889fSRafael J. Wysocki } 168f721889fSRafael J. Wysocki 169f721889fSRafael J. Wysocki /** 170f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 171f721889fSRafael J. Wysocki * @dle: Device list entry of the device to restore the state of. 172f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 173f721889fSRafael J. Wysocki */ 174f721889fSRafael J. Wysocki static void __pm_genpd_restore_device(struct dev_list_entry *dle, 175f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 17617b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 177f721889fSRafael J. Wysocki { 178f721889fSRafael J. Wysocki struct device *dev = dle->dev; 179f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 180f721889fSRafael J. Wysocki 181f721889fSRafael J. Wysocki if (!dle->need_restore) 182f721889fSRafael J. Wysocki return; 183f721889fSRafael J. Wysocki 18417b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 18517b75ecaSRafael J. Wysocki 186f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_resume) { 187f721889fSRafael J. Wysocki if (genpd->start_device) 188f721889fSRafael J. Wysocki genpd->start_device(dev); 189f721889fSRafael J. Wysocki 190f721889fSRafael J. Wysocki drv->pm->runtime_resume(dev); 191f721889fSRafael J. Wysocki 192f721889fSRafael J. Wysocki if (genpd->stop_device) 193f721889fSRafael J. Wysocki genpd->stop_device(dev); 194f721889fSRafael J. Wysocki } 195f721889fSRafael J. Wysocki 19617b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 19717b75ecaSRafael J. Wysocki 198f721889fSRafael J. Wysocki dle->need_restore = false; 199f721889fSRafael J. Wysocki } 200f721889fSRafael J. Wysocki 201f721889fSRafael J. Wysocki /** 202c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 203c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 204c6d22b37SRafael J. Wysocki * 205c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 206c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 207c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 208c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 209c6d22b37SRafael J. Wysocki */ 210c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 211c6d22b37SRafael J. Wysocki { 212c6d22b37SRafael J. Wysocki return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 213c6d22b37SRafael J. Wysocki } 214c6d22b37SRafael J. Wysocki 215c6d22b37SRafael J. Wysocki /** 216f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 217f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 218f721889fSRafael J. Wysocki * 219f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 220f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 221f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 222f721889fSRafael J. Wysocki */ 223f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 22417b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 225f721889fSRafael J. Wysocki { 226f721889fSRafael J. Wysocki struct generic_pm_domain *parent; 227f721889fSRafael J. Wysocki struct dev_list_entry *dle; 228f721889fSRafael J. Wysocki unsigned int not_suspended; 229c6d22b37SRafael J. Wysocki int ret = 0; 230f721889fSRafael J. Wysocki 231c6d22b37SRafael J. Wysocki start: 232c6d22b37SRafael J. Wysocki /* 233c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 234c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 235c6d22b37SRafael J. Wysocki * (2) System suspend is in progress. 236c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 237c6d22b37SRafael J. Wysocki */ 238c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0 239c6d22b37SRafael J. Wysocki || genpd->resume_count > 0) 240f721889fSRafael J. Wysocki return 0; 241f721889fSRafael J. Wysocki 242f721889fSRafael J. Wysocki if (genpd->sd_count > 0) 243f721889fSRafael J. Wysocki return -EBUSY; 244f721889fSRafael J. Wysocki 245f721889fSRafael J. Wysocki not_suspended = 0; 246f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) 247f721889fSRafael J. Wysocki if (dle->dev->driver && !pm_runtime_suspended(dle->dev)) 248f721889fSRafael J. Wysocki not_suspended++; 249f721889fSRafael J. Wysocki 250f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 251f721889fSRafael J. Wysocki return -EBUSY; 252f721889fSRafael J. Wysocki 253c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 254c6d22b37SRafael J. Wysocki /* 255c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 256c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 257c6d22b37SRafael J. Wysocki */ 258c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 259c6d22b37SRafael J. Wysocki return 0; 260c6d22b37SRafael J. Wysocki } 261c6d22b37SRafael J. Wysocki 262f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 263f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 264f721889fSRafael J. Wysocki return -EAGAIN; 265f721889fSRafael J. Wysocki } 266f721889fSRafael J. Wysocki 26717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 268c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 26917b75ecaSRafael J. Wysocki 270f721889fSRafael J. Wysocki list_for_each_entry_reverse(dle, &genpd->dev_list, node) { 271f721889fSRafael J. Wysocki ret = __pm_genpd_save_device(dle, genpd); 272697a7f37SRafael J. Wysocki if (ret) { 273697a7f37SRafael J. Wysocki genpd_set_active(genpd); 274697a7f37SRafael J. Wysocki goto out; 275697a7f37SRafael J. Wysocki } 276f721889fSRafael J. Wysocki 277c6d22b37SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 278c6d22b37SRafael J. Wysocki goto out; 279c6d22b37SRafael J. Wysocki 280c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 281c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 282c6d22b37SRafael J. Wysocki goto start; 283c6d22b37SRafael J. Wysocki } 284c6d22b37SRafael J. Wysocki } 28517b75ecaSRafael J. Wysocki 28617b75ecaSRafael J. Wysocki parent = genpd->parent; 28717b75ecaSRafael J. Wysocki if (parent) { 288c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 289c6d22b37SRafael J. Wysocki 29017b75ecaSRafael J. Wysocki genpd_acquire_lock(parent); 29117b75ecaSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 292c6d22b37SRafael J. Wysocki 293c6d22b37SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) { 294c6d22b37SRafael J. Wysocki genpd_release_lock(parent); 295c6d22b37SRafael J. Wysocki goto out; 296c6d22b37SRafael J. Wysocki } 29717b75ecaSRafael J. Wysocki } 29817b75ecaSRafael J. Wysocki 299f721889fSRafael J. Wysocki if (genpd->power_off) 300f721889fSRafael J. Wysocki genpd->power_off(genpd); 301f721889fSRafael J. Wysocki 30217b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 303f721889fSRafael J. Wysocki 304f721889fSRafael J. Wysocki if (parent) { 305f721889fSRafael J. Wysocki genpd_sd_counter_dec(parent); 306f721889fSRafael J. Wysocki if (parent->sd_count == 0) 307f721889fSRafael J. Wysocki queue_work(pm_wq, &parent->power_off_work); 30817b75ecaSRafael J. Wysocki 30917b75ecaSRafael J. Wysocki genpd_release_lock(parent); 310f721889fSRafael J. Wysocki } 311f721889fSRafael J. Wysocki 312c6d22b37SRafael J. Wysocki out: 313c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 314c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 315c6d22b37SRafael J. Wysocki return ret; 316f721889fSRafael J. Wysocki } 317f721889fSRafael J. Wysocki 318f721889fSRafael J. Wysocki /** 319f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 320f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 321f721889fSRafael J. Wysocki */ 322f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 323f721889fSRafael J. Wysocki { 324f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 325f721889fSRafael J. Wysocki 326f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 327f721889fSRafael J. Wysocki 32817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 329f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 33017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 331f721889fSRafael J. Wysocki } 332f721889fSRafael J. Wysocki 333f721889fSRafael J. Wysocki /** 334f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 335f721889fSRafael J. Wysocki * @dev: Device to suspend. 336f721889fSRafael J. Wysocki * 337f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 338f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 339f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 340f721889fSRafael J. Wysocki */ 341f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 342f721889fSRafael J. Wysocki { 343f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 344f721889fSRafael J. Wysocki 345f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 346f721889fSRafael J. Wysocki 3475248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 3485248051bSRafael J. Wysocki if (IS_ERR(genpd)) 349f721889fSRafael J. Wysocki return -EINVAL; 350f721889fSRafael J. Wysocki 351f721889fSRafael J. Wysocki if (genpd->stop_device) { 352f721889fSRafael J. Wysocki int ret = genpd->stop_device(dev); 353f721889fSRafael J. Wysocki if (ret) 35417b75ecaSRafael J. Wysocki return ret; 355f721889fSRafael J. Wysocki } 35617b75ecaSRafael J. Wysocki 357c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 358f721889fSRafael J. Wysocki genpd->in_progress++; 359f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 360f721889fSRafael J. Wysocki genpd->in_progress--; 361c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 362f721889fSRafael J. Wysocki 363f721889fSRafael J. Wysocki return 0; 364f721889fSRafael J. Wysocki } 365f721889fSRafael J. Wysocki 366f721889fSRafael J. Wysocki /** 367596ba34bSRafael J. Wysocki * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 368596ba34bSRafael J. Wysocki * @dev: Device to resume. 369596ba34bSRafael J. Wysocki * @genpd: PM domain the device belongs to. 370596ba34bSRafael J. Wysocki */ 371596ba34bSRafael J. Wysocki static void __pm_genpd_runtime_resume(struct device *dev, 372596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd) 373596ba34bSRafael J. Wysocki { 374596ba34bSRafael J. Wysocki struct dev_list_entry *dle; 375596ba34bSRafael J. Wysocki 376596ba34bSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) { 377596ba34bSRafael J. Wysocki if (dle->dev == dev) { 378596ba34bSRafael J. Wysocki __pm_genpd_restore_device(dle, genpd); 379596ba34bSRafael J. Wysocki break; 380596ba34bSRafael J. Wysocki } 381596ba34bSRafael J. Wysocki } 382596ba34bSRafael J. Wysocki } 383596ba34bSRafael J. Wysocki 384596ba34bSRafael J. Wysocki /** 385f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 386f721889fSRafael J. Wysocki * @dev: Device to resume. 387f721889fSRafael J. Wysocki * 388f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 389f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 390f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 391f721889fSRafael J. Wysocki */ 392f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 393f721889fSRafael J. Wysocki { 394f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 395c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 396f721889fSRafael J. Wysocki int ret; 397f721889fSRafael J. Wysocki 398f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 399f721889fSRafael J. Wysocki 4005248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4015248051bSRafael J. Wysocki if (IS_ERR(genpd)) 402f721889fSRafael J. Wysocki return -EINVAL; 403f721889fSRafael J. Wysocki 404f721889fSRafael J. Wysocki ret = pm_genpd_poweron(genpd); 405f721889fSRafael J. Wysocki if (ret) 406f721889fSRafael J. Wysocki return ret; 407f721889fSRafael J. Wysocki 408c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 40917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 410c6d22b37SRafael J. Wysocki genpd->resume_count++; 411c6d22b37SRafael J. Wysocki for (;;) { 412c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 413c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 414c6d22b37SRafael J. Wysocki /* 415c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 416c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 417c6d22b37SRafael J. Wysocki * not wait. 418c6d22b37SRafael J. Wysocki */ 419c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 420c6d22b37SRafael J. Wysocki break; 421c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 422c6d22b37SRafael J. Wysocki 423c6d22b37SRafael J. Wysocki schedule(); 424c6d22b37SRafael J. Wysocki 425c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 426c6d22b37SRafael J. Wysocki } 427c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 428596ba34bSRafael J. Wysocki __pm_genpd_runtime_resume(dev, genpd); 429c6d22b37SRafael J. Wysocki genpd->resume_count--; 430c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 43117b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 432c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 43317b75ecaSRafael J. Wysocki 43417b75ecaSRafael J. Wysocki if (genpd->start_device) 43517b75ecaSRafael J. Wysocki genpd->start_device(dev); 436f721889fSRafael J. Wysocki 437f721889fSRafael J. Wysocki return 0; 438f721889fSRafael J. Wysocki } 439f721889fSRafael J. Wysocki 440f721889fSRafael J. Wysocki #else 441f721889fSRafael J. Wysocki 442f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 443596ba34bSRafael J. Wysocki static inline void __pm_genpd_runtime_resume(struct device *dev, 444596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd) {} 445f721889fSRafael J. Wysocki 446f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 447f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 448f721889fSRafael J. Wysocki 449f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 450f721889fSRafael J. Wysocki 451596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 452596ba34bSRafael J. Wysocki 453596ba34bSRafael J. Wysocki /** 454596ba34bSRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents. 455596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 456596ba34bSRafael J. Wysocki * 457596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 458596ba34bSRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its parent. 459596ba34bSRafael J. Wysocki * 460596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 461596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 462596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 463596ba34bSRafael J. Wysocki */ 464596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 465596ba34bSRafael J. Wysocki { 466596ba34bSRafael J. Wysocki struct generic_pm_domain *parent = genpd->parent; 467596ba34bSRafael J. Wysocki 46817b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 469596ba34bSRafael J. Wysocki return; 470596ba34bSRafael J. Wysocki 471596ba34bSRafael J. Wysocki if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0) 472596ba34bSRafael J. Wysocki return; 473596ba34bSRafael J. Wysocki 474596ba34bSRafael J. Wysocki if (genpd->power_off) 475596ba34bSRafael J. Wysocki genpd->power_off(genpd); 476596ba34bSRafael J. Wysocki 47717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 478596ba34bSRafael J. Wysocki if (parent) { 479596ba34bSRafael J. Wysocki genpd_sd_counter_dec(parent); 480596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(parent); 481596ba34bSRafael J. Wysocki } 482596ba34bSRafael J. Wysocki } 483596ba34bSRafael J. Wysocki 484596ba34bSRafael J. Wysocki /** 485596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 486596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 487596ba34bSRafael J. Wysocki * 488596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 489596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 490596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 491596ba34bSRafael J. Wysocki * consisting of I/O devices. 492596ba34bSRafael J. Wysocki */ 493596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 494596ba34bSRafael J. Wysocki { 495596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 496b6c10c84SRafael J. Wysocki int ret; 497596ba34bSRafael J. Wysocki 498596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 499596ba34bSRafael J. Wysocki 500596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 501596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 502596ba34bSRafael J. Wysocki return -EINVAL; 503596ba34bSRafael J. Wysocki 50417b75ecaSRafael J. Wysocki /* 50517b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 50617b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 50717b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 50817b75ecaSRafael J. Wysocki */ 50917b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 51017b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 51117b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 51217b75ecaSRafael J. Wysocki 51317b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 51417b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 51517b75ecaSRafael J. Wysocki return -EBUSY; 51617b75ecaSRafael J. Wysocki } 51717b75ecaSRafael J. Wysocki 51817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 519596ba34bSRafael J. Wysocki 520596ba34bSRafael J. Wysocki if (genpd->prepared_count++ == 0) 52117b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 52217b75ecaSRafael J. Wysocki 52317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 524596ba34bSRafael J. Wysocki 525596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 52617b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 527596ba34bSRafael J. Wysocki return 0; 528596ba34bSRafael J. Wysocki } 529596ba34bSRafael J. Wysocki 530596ba34bSRafael J. Wysocki /* 53117b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 53217b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 53317b75ecaSRafael J. Wysocki * is suspended (e.g. it's been stopped by .stop_device()), we need 53417b75ecaSRafael J. Wysocki * to make it operational. 535596ba34bSRafael J. Wysocki */ 53617b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 537596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 538596ba34bSRafael J. Wysocki 539b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 540b6c10c84SRafael J. Wysocki if (ret) { 541b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 542b6c10c84SRafael J. Wysocki 543b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 544b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 545b6c10c84SRafael J. Wysocki 546b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 54717b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 548b6c10c84SRafael J. Wysocki } 54917b75ecaSRafael J. Wysocki 55017b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 551b6c10c84SRafael J. Wysocki return ret; 552596ba34bSRafael J. Wysocki } 553596ba34bSRafael J. Wysocki 554596ba34bSRafael J. Wysocki /** 555596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 556596ba34bSRafael J. Wysocki * @dev: Device to suspend. 557596ba34bSRafael J. Wysocki * 558596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 559596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 560596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 561596ba34bSRafael J. Wysocki */ 562596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 563596ba34bSRafael J. Wysocki { 564596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 565596ba34bSRafael J. Wysocki 566596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 567596ba34bSRafael J. Wysocki 568596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 569596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 570596ba34bSRafael J. Wysocki return -EINVAL; 571596ba34bSRafael J. Wysocki 572596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 573596ba34bSRafael J. Wysocki } 574596ba34bSRafael J. Wysocki 575596ba34bSRafael J. Wysocki /** 576596ba34bSRafael J. Wysocki * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain. 577596ba34bSRafael J. Wysocki * @dev: Device to suspend. 578596ba34bSRafael J. Wysocki * 579596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 580596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 581596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 582596ba34bSRafael J. Wysocki */ 583596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 584596ba34bSRafael J. Wysocki { 585596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 586596ba34bSRafael J. Wysocki int ret; 587596ba34bSRafael J. Wysocki 588596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 589596ba34bSRafael J. Wysocki 590596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 591596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 592596ba34bSRafael J. Wysocki return -EINVAL; 593596ba34bSRafael J. Wysocki 594596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 595596ba34bSRafael J. Wysocki return 0; 596596ba34bSRafael J. Wysocki 597596ba34bSRafael J. Wysocki ret = pm_generic_suspend_noirq(dev); 598596ba34bSRafael J. Wysocki if (ret) 599596ba34bSRafael J. Wysocki return ret; 600596ba34bSRafael J. Wysocki 601d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 602d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 603d4f2d87aSRafael J. Wysocki return 0; 604d4f2d87aSRafael J. Wysocki 605596ba34bSRafael J. Wysocki if (genpd->stop_device) 606596ba34bSRafael J. Wysocki genpd->stop_device(dev); 607596ba34bSRafael J. Wysocki 608596ba34bSRafael J. Wysocki /* 609596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 610596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 611596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 612596ba34bSRafael J. Wysocki */ 613596ba34bSRafael J. Wysocki genpd->suspended_count++; 614596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 615596ba34bSRafael J. Wysocki 616596ba34bSRafael J. Wysocki return 0; 617596ba34bSRafael J. Wysocki } 618596ba34bSRafael J. Wysocki 619596ba34bSRafael J. Wysocki /** 620596ba34bSRafael J. Wysocki * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain. 621596ba34bSRafael J. Wysocki * @dev: Device to resume. 622596ba34bSRafael J. Wysocki * 623596ba34bSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 624596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 625596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 626596ba34bSRafael J. Wysocki * devices. 627596ba34bSRafael J. Wysocki */ 628596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 629596ba34bSRafael J. Wysocki { 630596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 631596ba34bSRafael J. Wysocki 632596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 633596ba34bSRafael J. Wysocki 634596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 635596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 636596ba34bSRafael J. Wysocki return -EINVAL; 637596ba34bSRafael J. Wysocki 638596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 639596ba34bSRafael J. Wysocki return 0; 640596ba34bSRafael J. Wysocki 641596ba34bSRafael J. Wysocki /* 642596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 643596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 644596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 645596ba34bSRafael J. Wysocki */ 646596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 647596ba34bSRafael J. Wysocki genpd->suspended_count--; 648596ba34bSRafael J. Wysocki if (genpd->start_device) 649596ba34bSRafael J. Wysocki genpd->start_device(dev); 650596ba34bSRafael J. Wysocki 651596ba34bSRafael J. Wysocki return pm_generic_resume_noirq(dev); 652596ba34bSRafael J. Wysocki } 653596ba34bSRafael J. Wysocki 654596ba34bSRafael J. Wysocki /** 655596ba34bSRafael J. Wysocki * pm_genpd_resume - Resume a device belonging to an I/O power domain. 656596ba34bSRafael J. Wysocki * @dev: Device to resume. 657596ba34bSRafael J. Wysocki * 658596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 659596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 660596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 661596ba34bSRafael J. Wysocki */ 662596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 663596ba34bSRafael J. Wysocki { 664596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 665596ba34bSRafael J. Wysocki 666596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 667596ba34bSRafael J. Wysocki 668596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 669596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 670596ba34bSRafael J. Wysocki return -EINVAL; 671596ba34bSRafael J. Wysocki 672596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 673596ba34bSRafael J. Wysocki } 674596ba34bSRafael J. Wysocki 675596ba34bSRafael J. Wysocki /** 676596ba34bSRafael J. Wysocki * pm_genpd_freeze - Freeze a device belonging to an I/O power domain. 677596ba34bSRafael J. Wysocki * @dev: Device to freeze. 678596ba34bSRafael J. Wysocki * 679596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 680596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 681596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 682596ba34bSRafael J. Wysocki */ 683596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 684596ba34bSRafael J. Wysocki { 685596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 686596ba34bSRafael J. Wysocki 687596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 688596ba34bSRafael J. Wysocki 689596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 690596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 691596ba34bSRafael J. Wysocki return -EINVAL; 692596ba34bSRafael J. Wysocki 693596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 694596ba34bSRafael J. Wysocki } 695596ba34bSRafael J. Wysocki 696596ba34bSRafael J. Wysocki /** 697596ba34bSRafael J. Wysocki * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain. 698596ba34bSRafael J. Wysocki * @dev: Device to freeze. 699596ba34bSRafael J. Wysocki * 700596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 701596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 702596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 703596ba34bSRafael J. Wysocki * devices. 704596ba34bSRafael J. Wysocki */ 705596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 706596ba34bSRafael J. Wysocki { 707596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 708596ba34bSRafael J. Wysocki int ret; 709596ba34bSRafael J. Wysocki 710596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 711596ba34bSRafael J. Wysocki 712596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 713596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 714596ba34bSRafael J. Wysocki return -EINVAL; 715596ba34bSRafael J. Wysocki 716596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 717596ba34bSRafael J. Wysocki return 0; 718596ba34bSRafael J. Wysocki 719596ba34bSRafael J. Wysocki ret = pm_generic_freeze_noirq(dev); 720596ba34bSRafael J. Wysocki if (ret) 721596ba34bSRafael J. Wysocki return ret; 722596ba34bSRafael J. Wysocki 723596ba34bSRafael J. Wysocki if (genpd->stop_device) 724596ba34bSRafael J. Wysocki genpd->stop_device(dev); 725596ba34bSRafael J. Wysocki 726596ba34bSRafael J. Wysocki return 0; 727596ba34bSRafael J. Wysocki } 728596ba34bSRafael J. Wysocki 729596ba34bSRafael J. Wysocki /** 730596ba34bSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain. 731596ba34bSRafael J. Wysocki * @dev: Device to thaw. 732596ba34bSRafael J. Wysocki * 733596ba34bSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 734596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 735596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 736596ba34bSRafael J. Wysocki * devices. 737596ba34bSRafael J. Wysocki */ 738596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 739596ba34bSRafael J. Wysocki { 740596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 741596ba34bSRafael J. Wysocki 742596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 743596ba34bSRafael J. Wysocki 744596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 745596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 746596ba34bSRafael J. Wysocki return -EINVAL; 747596ba34bSRafael J. Wysocki 748596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 749596ba34bSRafael J. Wysocki return 0; 750596ba34bSRafael J. Wysocki 751596ba34bSRafael J. Wysocki if (genpd->start_device) 752596ba34bSRafael J. Wysocki genpd->start_device(dev); 753596ba34bSRafael J. Wysocki 754596ba34bSRafael J. Wysocki return pm_generic_thaw_noirq(dev); 755596ba34bSRafael J. Wysocki } 756596ba34bSRafael J. Wysocki 757596ba34bSRafael J. Wysocki /** 758596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 759596ba34bSRafael J. Wysocki * @dev: Device to thaw. 760596ba34bSRafael J. Wysocki * 761596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 762596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 763596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 764596ba34bSRafael J. Wysocki */ 765596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 766596ba34bSRafael J. Wysocki { 767596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 768596ba34bSRafael J. Wysocki 769596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 770596ba34bSRafael J. Wysocki 771596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 772596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 773596ba34bSRafael J. Wysocki return -EINVAL; 774596ba34bSRafael J. Wysocki 775596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 776596ba34bSRafael J. Wysocki } 777596ba34bSRafael J. Wysocki 778596ba34bSRafael J. Wysocki /** 779596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain. 780596ba34bSRafael J. Wysocki * @dev: Device to suspend. 781596ba34bSRafael J. Wysocki * 782596ba34bSRafael J. Wysocki * Power off a device under the assumption that its pm_domain field points to 783596ba34bSRafael J. Wysocki * the domain member of an object of type struct generic_pm_domain representing 784596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 785596ba34bSRafael J. Wysocki */ 786596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev) 787596ba34bSRafael J. Wysocki { 788596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 789596ba34bSRafael J. Wysocki 790596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 791596ba34bSRafael J. Wysocki 792596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 793596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 794596ba34bSRafael J. Wysocki return -EINVAL; 795596ba34bSRafael J. Wysocki 796596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev); 797596ba34bSRafael J. Wysocki } 798596ba34bSRafael J. Wysocki 799596ba34bSRafael J. Wysocki /** 800596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain. 801596ba34bSRafael J. Wysocki * @dev: Device to suspend. 802596ba34bSRafael J. Wysocki * 803596ba34bSRafael J. Wysocki * Carry out a late powering off of a device under the assumption that its 804596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 805596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 806596ba34bSRafael J. Wysocki */ 807596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev) 808596ba34bSRafael J. Wysocki { 809596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 810596ba34bSRafael J. Wysocki int ret; 811596ba34bSRafael J. Wysocki 812596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 813596ba34bSRafael J. Wysocki 814596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 815596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 816596ba34bSRafael J. Wysocki return -EINVAL; 817596ba34bSRafael J. Wysocki 818596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 819596ba34bSRafael J. Wysocki return 0; 820596ba34bSRafael J. Wysocki 821596ba34bSRafael J. Wysocki ret = pm_generic_poweroff_noirq(dev); 822596ba34bSRafael J. Wysocki if (ret) 823596ba34bSRafael J. Wysocki return ret; 824596ba34bSRafael J. Wysocki 825d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 826d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 827d4f2d87aSRafael J. Wysocki return 0; 828d4f2d87aSRafael J. Wysocki 829596ba34bSRafael J. Wysocki if (genpd->stop_device) 830596ba34bSRafael J. Wysocki genpd->stop_device(dev); 831596ba34bSRafael J. Wysocki 832596ba34bSRafael J. Wysocki /* 833596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 834596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 835596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 836596ba34bSRafael J. Wysocki */ 837596ba34bSRafael J. Wysocki genpd->suspended_count++; 838596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 839596ba34bSRafael J. Wysocki 840596ba34bSRafael J. Wysocki return 0; 841596ba34bSRafael J. Wysocki } 842596ba34bSRafael J. Wysocki 843596ba34bSRafael J. Wysocki /** 844596ba34bSRafael J. Wysocki * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain. 845596ba34bSRafael J. Wysocki * @dev: Device to resume. 846596ba34bSRafael J. Wysocki * 847596ba34bSRafael J. Wysocki * Carry out an early restore of a device under the assumption that its 848596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 849596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 850596ba34bSRafael J. Wysocki * devices. 851596ba34bSRafael J. Wysocki */ 852596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 853596ba34bSRafael J. Wysocki { 854596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 855596ba34bSRafael J. Wysocki 856596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 857596ba34bSRafael J. Wysocki 858596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 859596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 860596ba34bSRafael J. Wysocki return -EINVAL; 861596ba34bSRafael J. Wysocki 862596ba34bSRafael J. Wysocki /* 863596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 864596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 865596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 866596ba34bSRafael J. Wysocki */ 86717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 868596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 869596ba34bSRafael J. Wysocki /* 870596ba34bSRafael J. Wysocki * The boot kernel might put the domain into the power on state, 871596ba34bSRafael J. Wysocki * so make sure it really is powered off. 872596ba34bSRafael J. Wysocki */ 873596ba34bSRafael J. Wysocki if (genpd->power_off) 874596ba34bSRafael J. Wysocki genpd->power_off(genpd); 875596ba34bSRafael J. Wysocki return 0; 876596ba34bSRafael J. Wysocki } 877596ba34bSRafael J. Wysocki 878596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 879596ba34bSRafael J. Wysocki genpd->suspended_count--; 880596ba34bSRafael J. Wysocki if (genpd->start_device) 881596ba34bSRafael J. Wysocki genpd->start_device(dev); 882596ba34bSRafael J. Wysocki 883596ba34bSRafael J. Wysocki return pm_generic_restore_noirq(dev); 884596ba34bSRafael J. Wysocki } 885596ba34bSRafael J. Wysocki 886596ba34bSRafael J. Wysocki /** 887596ba34bSRafael J. Wysocki * pm_genpd_restore - Restore a device belonging to an I/O power domain. 888596ba34bSRafael J. Wysocki * @dev: Device to resume. 889596ba34bSRafael J. Wysocki * 890596ba34bSRafael J. Wysocki * Restore a device under the assumption that its pm_domain field points to the 891596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 892596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 893596ba34bSRafael J. Wysocki */ 894596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev) 895596ba34bSRafael J. Wysocki { 896596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 897596ba34bSRafael J. Wysocki 898596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 899596ba34bSRafael J. Wysocki 900596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 901596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 902596ba34bSRafael J. Wysocki return -EINVAL; 903596ba34bSRafael J. Wysocki 904596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_restore(dev); 905596ba34bSRafael J. Wysocki } 906596ba34bSRafael J. Wysocki 907596ba34bSRafael J. Wysocki /** 908596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 909596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 910596ba34bSRafael J. Wysocki * 911596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 912596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 913596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 914596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 915596ba34bSRafael J. Wysocki */ 916596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 917596ba34bSRafael J. Wysocki { 918596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 919596ba34bSRafael J. Wysocki bool run_complete; 920596ba34bSRafael J. Wysocki 921596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 922596ba34bSRafael J. Wysocki 923596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 924596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 925596ba34bSRafael J. Wysocki return; 926596ba34bSRafael J. Wysocki 927596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 928596ba34bSRafael J. Wysocki 929596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 930596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 931596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 932596ba34bSRafael J. Wysocki 933596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 934596ba34bSRafael J. Wysocki 935596ba34bSRafael J. Wysocki if (run_complete) { 936596ba34bSRafael J. Wysocki pm_generic_complete(dev); 9376f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 938596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 9396f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 940596ba34bSRafael J. Wysocki } 941596ba34bSRafael J. Wysocki } 942596ba34bSRafael J. Wysocki 943596ba34bSRafael J. Wysocki #else 944596ba34bSRafael J. Wysocki 945596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 946596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 947596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 948596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 949596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 950596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 951596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 952596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 953596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 954596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq NULL 955596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff NULL 956596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 957596ba34bSRafael J. Wysocki #define pm_genpd_restore NULL 958596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 959596ba34bSRafael J. Wysocki 960596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 961596ba34bSRafael J. Wysocki 962f721889fSRafael J. Wysocki /** 963f721889fSRafael J. Wysocki * pm_genpd_add_device - Add a device to an I/O PM domain. 964f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 965f721889fSRafael J. Wysocki * @dev: Device to be added. 966f721889fSRafael J. Wysocki */ 967f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 968f721889fSRafael J. Wysocki { 969f721889fSRafael J. Wysocki struct dev_list_entry *dle; 970f721889fSRafael J. Wysocki int ret = 0; 971f721889fSRafael J. Wysocki 972f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 973f721889fSRafael J. Wysocki 974f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 975f721889fSRafael J. Wysocki return -EINVAL; 976f721889fSRafael J. Wysocki 97717b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 978f721889fSRafael J. Wysocki 97917b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) { 980f721889fSRafael J. Wysocki ret = -EINVAL; 981f721889fSRafael J. Wysocki goto out; 982f721889fSRafael J. Wysocki } 983f721889fSRafael J. Wysocki 984596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 985596ba34bSRafael J. Wysocki ret = -EAGAIN; 986596ba34bSRafael J. Wysocki goto out; 987596ba34bSRafael J. Wysocki } 988596ba34bSRafael J. Wysocki 989f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) 990f721889fSRafael J. Wysocki if (dle->dev == dev) { 991f721889fSRafael J. Wysocki ret = -EINVAL; 992f721889fSRafael J. Wysocki goto out; 993f721889fSRafael J. Wysocki } 994f721889fSRafael J. Wysocki 995f721889fSRafael J. Wysocki dle = kzalloc(sizeof(*dle), GFP_KERNEL); 996f721889fSRafael J. Wysocki if (!dle) { 997f721889fSRafael J. Wysocki ret = -ENOMEM; 998f721889fSRafael J. Wysocki goto out; 999f721889fSRafael J. Wysocki } 1000f721889fSRafael J. Wysocki 1001f721889fSRafael J. Wysocki dle->dev = dev; 1002f721889fSRafael J. Wysocki dle->need_restore = false; 1003f721889fSRafael J. Wysocki list_add_tail(&dle->node, &genpd->dev_list); 1004596ba34bSRafael J. Wysocki genpd->device_count++; 1005f721889fSRafael J. Wysocki 1006f721889fSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1007f721889fSRafael J. Wysocki dev->pm_domain = &genpd->domain; 1008f721889fSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1009f721889fSRafael J. Wysocki 1010f721889fSRafael J. Wysocki out: 101117b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1012f721889fSRafael J. Wysocki 1013f721889fSRafael J. Wysocki return ret; 1014f721889fSRafael J. Wysocki } 1015f721889fSRafael J. Wysocki 1016f721889fSRafael J. Wysocki /** 1017f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1018f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1019f721889fSRafael J. Wysocki * @dev: Device to be removed. 1020f721889fSRafael J. Wysocki */ 1021f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1022f721889fSRafael J. Wysocki struct device *dev) 1023f721889fSRafael J. Wysocki { 1024f721889fSRafael J. Wysocki struct dev_list_entry *dle; 1025f721889fSRafael J. Wysocki int ret = -EINVAL; 1026f721889fSRafael J. Wysocki 1027f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1028f721889fSRafael J. Wysocki 1029f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1030f721889fSRafael J. Wysocki return -EINVAL; 1031f721889fSRafael J. Wysocki 103217b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1033f721889fSRafael J. Wysocki 1034596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1035596ba34bSRafael J. Wysocki ret = -EAGAIN; 1036596ba34bSRafael J. Wysocki goto out; 1037596ba34bSRafael J. Wysocki } 1038596ba34bSRafael J. Wysocki 1039f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) { 1040f721889fSRafael J. Wysocki if (dle->dev != dev) 1041f721889fSRafael J. Wysocki continue; 1042f721889fSRafael J. Wysocki 1043f721889fSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1044f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1045f721889fSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1046f721889fSRafael J. Wysocki 1047596ba34bSRafael J. Wysocki genpd->device_count--; 1048f721889fSRafael J. Wysocki list_del(&dle->node); 1049f721889fSRafael J. Wysocki kfree(dle); 1050f721889fSRafael J. Wysocki 1051f721889fSRafael J. Wysocki ret = 0; 1052f721889fSRafael J. Wysocki break; 1053f721889fSRafael J. Wysocki } 1054f721889fSRafael J. Wysocki 1055596ba34bSRafael J. Wysocki out: 105617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1057f721889fSRafael J. Wysocki 1058f721889fSRafael J. Wysocki return ret; 1059f721889fSRafael J. Wysocki } 1060f721889fSRafael J. Wysocki 1061f721889fSRafael J. Wysocki /** 1062f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1063f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1064f721889fSRafael J. Wysocki * @new_subdomain: Subdomain to be added. 1065f721889fSRafael J. Wysocki */ 1066f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1067f721889fSRafael J. Wysocki struct generic_pm_domain *new_subdomain) 1068f721889fSRafael J. Wysocki { 1069f721889fSRafael J. Wysocki struct generic_pm_domain *subdomain; 1070f721889fSRafael J. Wysocki int ret = 0; 1071f721889fSRafael J. Wysocki 1072f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain)) 1073f721889fSRafael J. Wysocki return -EINVAL; 1074f721889fSRafael J. Wysocki 107517b75ecaSRafael J. Wysocki start: 107617b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 107717b75ecaSRafael J. Wysocki mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING); 1078f721889fSRafael J. Wysocki 107917b75ecaSRafael J. Wysocki if (new_subdomain->status != GPD_STATE_POWER_OFF 108017b75ecaSRafael J. Wysocki && new_subdomain->status != GPD_STATE_ACTIVE) { 108117b75ecaSRafael J. Wysocki mutex_unlock(&new_subdomain->lock); 108217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 108317b75ecaSRafael J. Wysocki goto start; 108417b75ecaSRafael J. Wysocki } 108517b75ecaSRafael J. Wysocki 108617b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 108717b75ecaSRafael J. Wysocki && new_subdomain->status != GPD_STATE_POWER_OFF) { 1088f721889fSRafael J. Wysocki ret = -EINVAL; 1089f721889fSRafael J. Wysocki goto out; 1090f721889fSRafael J. Wysocki } 1091f721889fSRafael J. Wysocki 1092f721889fSRafael J. Wysocki list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 1093f721889fSRafael J. Wysocki if (subdomain == new_subdomain) { 1094f721889fSRafael J. Wysocki ret = -EINVAL; 1095f721889fSRafael J. Wysocki goto out; 1096f721889fSRafael J. Wysocki } 1097f721889fSRafael J. Wysocki } 1098f721889fSRafael J. Wysocki 1099f721889fSRafael J. Wysocki list_add_tail(&new_subdomain->sd_node, &genpd->sd_list); 1100f721889fSRafael J. Wysocki new_subdomain->parent = genpd; 110117b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1102f721889fSRafael J. Wysocki genpd->sd_count++; 1103f721889fSRafael J. Wysocki 1104f721889fSRafael J. Wysocki out: 110517b75ecaSRafael J. Wysocki mutex_unlock(&new_subdomain->lock); 110617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1107f721889fSRafael J. Wysocki 1108f721889fSRafael J. Wysocki return ret; 1109f721889fSRafael J. Wysocki } 1110f721889fSRafael J. Wysocki 1111f721889fSRafael J. Wysocki /** 1112f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1113f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 1114f721889fSRafael J. Wysocki * @target: Subdomain to be removed. 1115f721889fSRafael J. Wysocki */ 1116f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 1117f721889fSRafael J. Wysocki struct generic_pm_domain *target) 1118f721889fSRafael J. Wysocki { 1119f721889fSRafael J. Wysocki struct generic_pm_domain *subdomain; 1120f721889fSRafael J. Wysocki int ret = -EINVAL; 1121f721889fSRafael J. Wysocki 1122f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target)) 1123f721889fSRafael J. Wysocki return -EINVAL; 1124f721889fSRafael J. Wysocki 112517b75ecaSRafael J. Wysocki start: 112617b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1127f721889fSRafael J. Wysocki 1128f721889fSRafael J. Wysocki list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 1129f721889fSRafael J. Wysocki if (subdomain != target) 1130f721889fSRafael J. Wysocki continue; 1131f721889fSRafael J. Wysocki 1132f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1133f721889fSRafael J. Wysocki 113417b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 113517b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 113617b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 113717b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 113817b75ecaSRafael J. Wysocki goto start; 113917b75ecaSRafael J. Wysocki } 114017b75ecaSRafael J. Wysocki 1141f721889fSRafael J. Wysocki list_del(&subdomain->sd_node); 1142f721889fSRafael J. Wysocki subdomain->parent = NULL; 114317b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1144f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1145f721889fSRafael J. Wysocki 1146f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1147f721889fSRafael J. Wysocki 1148f721889fSRafael J. Wysocki ret = 0; 1149f721889fSRafael J. Wysocki break; 1150f721889fSRafael J. Wysocki } 1151f721889fSRafael J. Wysocki 115217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1153f721889fSRafael J. Wysocki 1154f721889fSRafael J. Wysocki return ret; 1155f721889fSRafael J. Wysocki } 1156f721889fSRafael J. Wysocki 1157f721889fSRafael J. Wysocki /** 1158f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1159f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1160f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1161f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1162f721889fSRafael J. Wysocki */ 1163f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1164f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1165f721889fSRafael J. Wysocki { 1166f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1167f721889fSRafael J. Wysocki return; 1168f721889fSRafael J. Wysocki 1169f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->sd_node); 1170f721889fSRafael J. Wysocki genpd->parent = NULL; 1171f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1172f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->sd_list); 1173f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1174f721889fSRafael J. Wysocki genpd->gov = gov; 1175f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1176f721889fSRafael J. Wysocki genpd->in_progress = 0; 1177f721889fSRafael J. Wysocki genpd->sd_count = 0; 117817b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 117917b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 1180c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 1181c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 1182596ba34bSRafael J. Wysocki genpd->device_count = 0; 1183596ba34bSRafael J. Wysocki genpd->suspended_count = 0; 1184f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1185f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1186f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1187596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1188596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 1189596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1190596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1191596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1192596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 1193596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1194596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1195596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1196596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_dev_poweroff; 1197596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq; 1198596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1199596ba34bSRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_restore; 1200596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1201f721889fSRafael J. Wysocki } 1202