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> 16f721889fSRafael J. Wysocki 175248051bSRafael J. Wysocki #ifdef CONFIG_PM 185248051bSRafael J. Wysocki 195248051bSRafael J. Wysocki static struct generic_pm_domain *dev_to_genpd(struct device *dev) 205248051bSRafael J. Wysocki { 215248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 225248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 235248051bSRafael J. Wysocki 24596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 255248051bSRafael J. Wysocki } 26f721889fSRafael J. Wysocki 27f721889fSRafael J. Wysocki static void genpd_sd_counter_dec(struct generic_pm_domain *genpd) 28f721889fSRafael J. Wysocki { 29f721889fSRafael J. Wysocki if (!WARN_ON(genpd->sd_count == 0)) 30f721889fSRafael J. Wysocki genpd->sd_count--; 31f721889fSRafael J. Wysocki } 32f721889fSRafael J. Wysocki 33f721889fSRafael J. Wysocki /** 345248051bSRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its parents. 355248051bSRafael J. Wysocki * @genpd: PM domain to power up. 365248051bSRafael J. Wysocki * 375248051bSRafael J. Wysocki * Restore power to @genpd and all of its parents so that it is possible to 385248051bSRafael J. Wysocki * resume a device belonging to it. 395248051bSRafael J. Wysocki */ 4018b4f3f5SMagnus Damm int pm_genpd_poweron(struct generic_pm_domain *genpd) 415248051bSRafael J. Wysocki { 425248051bSRafael J. Wysocki int ret = 0; 435248051bSRafael J. Wysocki 445248051bSRafael J. Wysocki start: 455248051bSRafael J. Wysocki if (genpd->parent) 465248051bSRafael J. Wysocki mutex_lock(&genpd->parent->lock); 475248051bSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 485248051bSRafael J. Wysocki 49596ba34bSRafael J. Wysocki if (!genpd->power_is_off 50596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 515248051bSRafael J. Wysocki goto out; 525248051bSRafael J. Wysocki 535248051bSRafael J. Wysocki if (genpd->parent && genpd->parent->power_is_off) { 545248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 555248051bSRafael J. Wysocki mutex_unlock(&genpd->parent->lock); 565248051bSRafael J. Wysocki 575248051bSRafael J. Wysocki ret = pm_genpd_poweron(genpd->parent); 585248051bSRafael J. Wysocki if (ret) 595248051bSRafael J. Wysocki return ret; 605248051bSRafael J. Wysocki 615248051bSRafael J. Wysocki goto start; 625248051bSRafael J. Wysocki } 635248051bSRafael J. Wysocki 645248051bSRafael J. Wysocki if (genpd->power_on) { 655248051bSRafael J. Wysocki int ret = genpd->power_on(genpd); 665248051bSRafael J. Wysocki if (ret) 675248051bSRafael J. Wysocki goto out; 685248051bSRafael J. Wysocki } 695248051bSRafael J. Wysocki 705248051bSRafael J. Wysocki genpd->power_is_off = false; 715248051bSRafael J. Wysocki if (genpd->parent) 725248051bSRafael J. Wysocki genpd->parent->sd_count++; 735248051bSRafael J. Wysocki 745248051bSRafael J. Wysocki out: 755248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 765248051bSRafael J. Wysocki if (genpd->parent) 775248051bSRafael J. Wysocki mutex_unlock(&genpd->parent->lock); 785248051bSRafael J. Wysocki 795248051bSRafael J. Wysocki return ret; 805248051bSRafael J. Wysocki } 815248051bSRafael J. Wysocki 825248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 835248051bSRafael J. Wysocki 845248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 855248051bSRafael J. Wysocki 865248051bSRafael J. Wysocki /** 87f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 88f721889fSRafael J. Wysocki * @dle: Device list entry of the device to save the state of. 89f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 90f721889fSRafael J. Wysocki */ 91f721889fSRafael J. Wysocki static int __pm_genpd_save_device(struct dev_list_entry *dle, 92f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 93f721889fSRafael J. Wysocki { 94f721889fSRafael J. Wysocki struct device *dev = dle->dev; 95f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 96f721889fSRafael J. Wysocki int ret = 0; 97f721889fSRafael J. Wysocki 98f721889fSRafael J. Wysocki if (dle->need_restore) 99f721889fSRafael J. Wysocki return 0; 100f721889fSRafael J. Wysocki 101f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_suspend) { 102f721889fSRafael J. Wysocki if (genpd->start_device) 103f721889fSRafael J. Wysocki genpd->start_device(dev); 104f721889fSRafael J. Wysocki 105f721889fSRafael J. Wysocki ret = drv->pm->runtime_suspend(dev); 106f721889fSRafael J. Wysocki 107f721889fSRafael J. Wysocki if (genpd->stop_device) 108f721889fSRafael J. Wysocki genpd->stop_device(dev); 109f721889fSRafael J. Wysocki } 110f721889fSRafael J. Wysocki 111f721889fSRafael J. Wysocki if (!ret) 112f721889fSRafael J. Wysocki dle->need_restore = true; 113f721889fSRafael J. Wysocki 114f721889fSRafael J. Wysocki return ret; 115f721889fSRafael J. Wysocki } 116f721889fSRafael J. Wysocki 117f721889fSRafael J. Wysocki /** 118f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 119f721889fSRafael J. Wysocki * @dle: Device list entry of the device to restore the state of. 120f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 121f721889fSRafael J. Wysocki */ 122f721889fSRafael J. Wysocki static void __pm_genpd_restore_device(struct dev_list_entry *dle, 123f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 124f721889fSRafael J. Wysocki { 125f721889fSRafael J. Wysocki struct device *dev = dle->dev; 126f721889fSRafael J. Wysocki struct device_driver *drv = dev->driver; 127f721889fSRafael J. Wysocki 128f721889fSRafael J. Wysocki if (!dle->need_restore) 129f721889fSRafael J. Wysocki return; 130f721889fSRafael J. Wysocki 131f721889fSRafael J. Wysocki if (drv && drv->pm && drv->pm->runtime_resume) { 132f721889fSRafael J. Wysocki if (genpd->start_device) 133f721889fSRafael J. Wysocki genpd->start_device(dev); 134f721889fSRafael J. Wysocki 135f721889fSRafael J. Wysocki drv->pm->runtime_resume(dev); 136f721889fSRafael J. Wysocki 137f721889fSRafael J. Wysocki if (genpd->stop_device) 138f721889fSRafael J. Wysocki genpd->stop_device(dev); 139f721889fSRafael J. Wysocki } 140f721889fSRafael J. Wysocki 141f721889fSRafael J. Wysocki dle->need_restore = false; 142f721889fSRafael J. Wysocki } 143f721889fSRafael J. Wysocki 144f721889fSRafael J. Wysocki /** 145f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 146f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 147f721889fSRafael J. Wysocki * 148f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 149f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 150f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 151f721889fSRafael J. Wysocki */ 152f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 153f721889fSRafael J. Wysocki { 154f721889fSRafael J. Wysocki struct generic_pm_domain *parent; 155f721889fSRafael J. Wysocki struct dev_list_entry *dle; 156f721889fSRafael J. Wysocki unsigned int not_suspended; 157f721889fSRafael J. Wysocki int ret; 158f721889fSRafael J. Wysocki 159596ba34bSRafael J. Wysocki if (genpd->power_is_off || genpd->prepared_count > 0) 160f721889fSRafael J. Wysocki return 0; 161f721889fSRafael J. Wysocki 162f721889fSRafael J. Wysocki if (genpd->sd_count > 0) 163f721889fSRafael J. Wysocki return -EBUSY; 164f721889fSRafael J. Wysocki 165f721889fSRafael J. Wysocki not_suspended = 0; 166f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) 167f721889fSRafael J. Wysocki if (dle->dev->driver && !pm_runtime_suspended(dle->dev)) 168f721889fSRafael J. Wysocki not_suspended++; 169f721889fSRafael J. Wysocki 170f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 171f721889fSRafael J. Wysocki return -EBUSY; 172f721889fSRafael J. Wysocki 173f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 174f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 175f721889fSRafael J. Wysocki return -EAGAIN; 176f721889fSRafael J. Wysocki } 177f721889fSRafael J. Wysocki 178f721889fSRafael J. Wysocki list_for_each_entry_reverse(dle, &genpd->dev_list, node) { 179f721889fSRafael J. Wysocki ret = __pm_genpd_save_device(dle, genpd); 180f721889fSRafael J. Wysocki if (ret) 181f721889fSRafael J. Wysocki goto err_dev; 182f721889fSRafael J. Wysocki } 183f721889fSRafael J. Wysocki 184f721889fSRafael J. Wysocki if (genpd->power_off) 185f721889fSRafael J. Wysocki genpd->power_off(genpd); 186f721889fSRafael J. Wysocki 187f721889fSRafael J. Wysocki genpd->power_is_off = true; 188f721889fSRafael J. Wysocki 189f721889fSRafael J. Wysocki parent = genpd->parent; 190f721889fSRafael J. Wysocki if (parent) { 191f721889fSRafael J. Wysocki genpd_sd_counter_dec(parent); 192f721889fSRafael J. Wysocki if (parent->sd_count == 0) 193f721889fSRafael J. Wysocki queue_work(pm_wq, &parent->power_off_work); 194f721889fSRafael J. Wysocki } 195f721889fSRafael J. Wysocki 196f721889fSRafael J. Wysocki return 0; 197f721889fSRafael J. Wysocki 198f721889fSRafael J. Wysocki err_dev: 199f721889fSRafael J. Wysocki list_for_each_entry_continue(dle, &genpd->dev_list, node) 200f721889fSRafael J. Wysocki __pm_genpd_restore_device(dle, genpd); 201f721889fSRafael J. Wysocki 202f721889fSRafael J. Wysocki return ret; 203f721889fSRafael J. Wysocki } 204f721889fSRafael J. Wysocki 205f721889fSRafael J. Wysocki /** 206f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 207f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 208f721889fSRafael J. Wysocki */ 209f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 210f721889fSRafael J. Wysocki { 211f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 212f721889fSRafael J. Wysocki 213f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 214f721889fSRafael J. Wysocki 215f721889fSRafael J. Wysocki if (genpd->parent) 216f721889fSRafael J. Wysocki mutex_lock(&genpd->parent->lock); 217f721889fSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 218f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 219f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 220f721889fSRafael J. Wysocki if (genpd->parent) 221f721889fSRafael J. Wysocki mutex_unlock(&genpd->parent->lock); 222f721889fSRafael J. Wysocki } 223f721889fSRafael J. Wysocki 224f721889fSRafael J. Wysocki /** 225f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 226f721889fSRafael J. Wysocki * @dev: Device to suspend. 227f721889fSRafael J. Wysocki * 228f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 229f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 230f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 231f721889fSRafael J. Wysocki */ 232f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 233f721889fSRafael J. Wysocki { 234f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 235f721889fSRafael J. Wysocki 236f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 237f721889fSRafael J. Wysocki 2385248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 2395248051bSRafael J. Wysocki if (IS_ERR(genpd)) 240f721889fSRafael J. Wysocki return -EINVAL; 241f721889fSRafael J. Wysocki 242f721889fSRafael J. Wysocki if (genpd->parent) 243f721889fSRafael J. Wysocki mutex_lock(&genpd->parent->lock); 244f721889fSRafael J. Wysocki mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 245f721889fSRafael J. Wysocki 246f721889fSRafael J. Wysocki if (genpd->stop_device) { 247f721889fSRafael J. Wysocki int ret = genpd->stop_device(dev); 248f721889fSRafael J. Wysocki if (ret) 249f721889fSRafael J. Wysocki goto out; 250f721889fSRafael J. Wysocki } 251f721889fSRafael J. Wysocki genpd->in_progress++; 252f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 253f721889fSRafael J. Wysocki genpd->in_progress--; 254f721889fSRafael J. Wysocki 255f721889fSRafael J. Wysocki out: 256f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 257f721889fSRafael J. Wysocki if (genpd->parent) 258f721889fSRafael J. Wysocki mutex_unlock(&genpd->parent->lock); 259f721889fSRafael J. Wysocki 260f721889fSRafael J. Wysocki return 0; 261f721889fSRafael J. Wysocki } 262f721889fSRafael J. Wysocki 263f721889fSRafael J. Wysocki /** 264596ba34bSRafael J. Wysocki * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 265596ba34bSRafael J. Wysocki * @dev: Device to resume. 266596ba34bSRafael J. Wysocki * @genpd: PM domain the device belongs to. 267596ba34bSRafael J. Wysocki */ 268596ba34bSRafael J. Wysocki static void __pm_genpd_runtime_resume(struct device *dev, 269596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd) 270596ba34bSRafael J. Wysocki { 271596ba34bSRafael J. Wysocki struct dev_list_entry *dle; 272596ba34bSRafael J. Wysocki 273596ba34bSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) { 274596ba34bSRafael J. Wysocki if (dle->dev == dev) { 275596ba34bSRafael J. Wysocki __pm_genpd_restore_device(dle, genpd); 276596ba34bSRafael J. Wysocki break; 277596ba34bSRafael J. Wysocki } 278596ba34bSRafael J. Wysocki } 279596ba34bSRafael J. Wysocki 280596ba34bSRafael J. Wysocki if (genpd->start_device) 281596ba34bSRafael J. Wysocki genpd->start_device(dev); 282596ba34bSRafael J. Wysocki } 283596ba34bSRafael J. Wysocki 284596ba34bSRafael J. Wysocki /** 285f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 286f721889fSRafael J. Wysocki * @dev: Device to resume. 287f721889fSRafael J. Wysocki * 288f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 289f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 290f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 291f721889fSRafael J. Wysocki */ 292f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 293f721889fSRafael J. Wysocki { 294f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 295f721889fSRafael J. Wysocki int ret; 296f721889fSRafael J. Wysocki 297f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 298f721889fSRafael J. Wysocki 2995248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 3005248051bSRafael J. Wysocki if (IS_ERR(genpd)) 301f721889fSRafael J. Wysocki return -EINVAL; 302f721889fSRafael J. Wysocki 303f721889fSRafael J. Wysocki ret = pm_genpd_poweron(genpd); 304f721889fSRafael J. Wysocki if (ret) 305f721889fSRafael J. Wysocki return ret; 306f721889fSRafael J. Wysocki 307f721889fSRafael J. Wysocki mutex_lock(&genpd->lock); 308596ba34bSRafael J. Wysocki __pm_genpd_runtime_resume(dev, genpd); 309f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 310f721889fSRafael J. Wysocki 311f721889fSRafael J. Wysocki return 0; 312f721889fSRafael J. Wysocki } 313f721889fSRafael J. Wysocki 314f721889fSRafael J. Wysocki #else 315f721889fSRafael J. Wysocki 316f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 317596ba34bSRafael J. Wysocki static inline void __pm_genpd_runtime_resume(struct device *dev, 318596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd) {} 319f721889fSRafael J. Wysocki 320f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 321f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 322f721889fSRafael J. Wysocki 323f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 324f721889fSRafael J. Wysocki 325596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 326596ba34bSRafael J. Wysocki 327596ba34bSRafael J. Wysocki /** 328596ba34bSRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents. 329596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 330596ba34bSRafael J. Wysocki * 331596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 332596ba34bSRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its parent. 333596ba34bSRafael J. Wysocki * 334596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 335596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 336596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 337596ba34bSRafael J. Wysocki */ 338596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 339596ba34bSRafael J. Wysocki { 340596ba34bSRafael J. Wysocki struct generic_pm_domain *parent = genpd->parent; 341596ba34bSRafael J. Wysocki 342596ba34bSRafael J. Wysocki if (genpd->power_is_off) 343596ba34bSRafael J. Wysocki return; 344596ba34bSRafael J. Wysocki 345596ba34bSRafael J. Wysocki if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0) 346596ba34bSRafael J. Wysocki return; 347596ba34bSRafael J. Wysocki 348596ba34bSRafael J. Wysocki if (genpd->power_off) 349596ba34bSRafael J. Wysocki genpd->power_off(genpd); 350596ba34bSRafael J. Wysocki 351596ba34bSRafael J. Wysocki genpd->power_is_off = true; 352596ba34bSRafael J. Wysocki if (parent) { 353596ba34bSRafael J. Wysocki genpd_sd_counter_dec(parent); 354596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(parent); 355596ba34bSRafael J. Wysocki } 356596ba34bSRafael J. Wysocki } 357596ba34bSRafael J. Wysocki 358596ba34bSRafael J. Wysocki /** 359596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 360596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 361596ba34bSRafael J. Wysocki * 362596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 363596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 364596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 365596ba34bSRafael J. Wysocki * consisting of I/O devices. 366596ba34bSRafael J. Wysocki */ 367596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 368596ba34bSRafael J. Wysocki { 369596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 370596ba34bSRafael J. Wysocki 371596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 372596ba34bSRafael J. Wysocki 373596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 374596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 375596ba34bSRafael J. Wysocki return -EINVAL; 376596ba34bSRafael J. Wysocki 377596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 378596ba34bSRafael J. Wysocki 379596ba34bSRafael J. Wysocki if (genpd->prepared_count++ == 0) 380596ba34bSRafael J. Wysocki genpd->suspend_power_off = genpd->power_is_off; 381596ba34bSRafael J. Wysocki 382596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 383596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 384596ba34bSRafael J. Wysocki return 0; 385596ba34bSRafael J. Wysocki } 386596ba34bSRafael J. Wysocki 387596ba34bSRafael J. Wysocki /* 388596ba34bSRafael J. Wysocki * If the device is in the (runtime) "suspended" state, call 389596ba34bSRafael J. Wysocki * .start_device() for it, if defined. 390596ba34bSRafael J. Wysocki */ 391596ba34bSRafael J. Wysocki if (pm_runtime_suspended(dev)) 392596ba34bSRafael J. Wysocki __pm_genpd_runtime_resume(dev, genpd); 393596ba34bSRafael J. Wysocki 394596ba34bSRafael J. Wysocki /* 395596ba34bSRafael J. Wysocki * Do not check if runtime resume is pending at this point, because it 396596ba34bSRafael J. Wysocki * has been taken care of already and if pm_genpd_poweron() ran at this 397596ba34bSRafael J. Wysocki * point as a result of the check, it would deadlock. 398596ba34bSRafael J. Wysocki */ 399596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 400596ba34bSRafael J. Wysocki 401596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 402596ba34bSRafael J. Wysocki 403596ba34bSRafael J. Wysocki return pm_generic_prepare(dev); 404596ba34bSRafael J. Wysocki } 405596ba34bSRafael J. Wysocki 406596ba34bSRafael J. Wysocki /** 407596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 408596ba34bSRafael J. Wysocki * @dev: Device to suspend. 409596ba34bSRafael J. Wysocki * 410596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 411596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 412596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 413596ba34bSRafael J. Wysocki */ 414596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 415596ba34bSRafael J. Wysocki { 416596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 417596ba34bSRafael J. Wysocki 418596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 419596ba34bSRafael J. Wysocki 420596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 421596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 422596ba34bSRafael J. Wysocki return -EINVAL; 423596ba34bSRafael J. Wysocki 424596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 425596ba34bSRafael J. Wysocki } 426596ba34bSRafael J. Wysocki 427596ba34bSRafael J. Wysocki /** 428596ba34bSRafael J. Wysocki * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain. 429596ba34bSRafael J. Wysocki * @dev: Device to suspend. 430596ba34bSRafael J. Wysocki * 431596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 432596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 433596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 434596ba34bSRafael J. Wysocki */ 435596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 436596ba34bSRafael J. Wysocki { 437596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 438596ba34bSRafael J. Wysocki int ret; 439596ba34bSRafael J. Wysocki 440596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 441596ba34bSRafael J. Wysocki 442596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 443596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 444596ba34bSRafael J. Wysocki return -EINVAL; 445596ba34bSRafael J. Wysocki 446596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 447596ba34bSRafael J. Wysocki return 0; 448596ba34bSRafael J. Wysocki 449596ba34bSRafael J. Wysocki ret = pm_generic_suspend_noirq(dev); 450596ba34bSRafael J. Wysocki if (ret) 451596ba34bSRafael J. Wysocki return ret; 452596ba34bSRafael J. Wysocki 453d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 454d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 455d4f2d87aSRafael J. Wysocki return 0; 456d4f2d87aSRafael J. Wysocki 457596ba34bSRafael J. Wysocki if (genpd->stop_device) 458596ba34bSRafael J. Wysocki genpd->stop_device(dev); 459596ba34bSRafael J. Wysocki 460596ba34bSRafael J. Wysocki /* 461596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 462596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 463596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 464596ba34bSRafael J. Wysocki */ 465596ba34bSRafael J. Wysocki genpd->suspended_count++; 466596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 467596ba34bSRafael J. Wysocki 468596ba34bSRafael J. Wysocki return 0; 469596ba34bSRafael J. Wysocki } 470596ba34bSRafael J. Wysocki 471596ba34bSRafael J. Wysocki /** 472596ba34bSRafael J. Wysocki * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain. 473596ba34bSRafael J. Wysocki * @dev: Device to resume. 474596ba34bSRafael J. Wysocki * 475596ba34bSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 476596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 477596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 478596ba34bSRafael J. Wysocki * devices. 479596ba34bSRafael J. Wysocki */ 480596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 481596ba34bSRafael J. Wysocki { 482596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 483596ba34bSRafael J. Wysocki 484596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 485596ba34bSRafael J. Wysocki 486596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 487596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 488596ba34bSRafael J. Wysocki return -EINVAL; 489596ba34bSRafael J. Wysocki 490596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 491596ba34bSRafael J. Wysocki return 0; 492596ba34bSRafael J. Wysocki 493596ba34bSRafael J. Wysocki /* 494596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 495596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 496596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 497596ba34bSRafael J. Wysocki */ 498596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 499596ba34bSRafael J. Wysocki genpd->suspended_count--; 500596ba34bSRafael J. Wysocki if (genpd->start_device) 501596ba34bSRafael J. Wysocki genpd->start_device(dev); 502596ba34bSRafael J. Wysocki 503596ba34bSRafael J. Wysocki return pm_generic_resume_noirq(dev); 504596ba34bSRafael J. Wysocki } 505596ba34bSRafael J. Wysocki 506596ba34bSRafael J. Wysocki /** 507596ba34bSRafael J. Wysocki * pm_genpd_resume - Resume a device belonging to an I/O power domain. 508596ba34bSRafael J. Wysocki * @dev: Device to resume. 509596ba34bSRafael J. Wysocki * 510596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 511596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 512596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 513596ba34bSRafael J. Wysocki */ 514596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 515596ba34bSRafael J. Wysocki { 516596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 517596ba34bSRafael J. Wysocki 518596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 519596ba34bSRafael J. Wysocki 520596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 521596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 522596ba34bSRafael J. Wysocki return -EINVAL; 523596ba34bSRafael J. Wysocki 524596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 525596ba34bSRafael J. Wysocki } 526596ba34bSRafael J. Wysocki 527596ba34bSRafael J. Wysocki /** 528596ba34bSRafael J. Wysocki * pm_genpd_freeze - Freeze a device belonging to an I/O power domain. 529596ba34bSRafael J. Wysocki * @dev: Device to freeze. 530596ba34bSRafael J. Wysocki * 531596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 532596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 533596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 534596ba34bSRafael J. Wysocki */ 535596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 536596ba34bSRafael J. Wysocki { 537596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 538596ba34bSRafael J. Wysocki 539596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 540596ba34bSRafael J. Wysocki 541596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 542596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 543596ba34bSRafael J. Wysocki return -EINVAL; 544596ba34bSRafael J. Wysocki 545596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 546596ba34bSRafael J. Wysocki } 547596ba34bSRafael J. Wysocki 548596ba34bSRafael J. Wysocki /** 549596ba34bSRafael J. Wysocki * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain. 550596ba34bSRafael J. Wysocki * @dev: Device to freeze. 551596ba34bSRafael J. Wysocki * 552596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 553596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 554596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 555596ba34bSRafael J. Wysocki * devices. 556596ba34bSRafael J. Wysocki */ 557596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 558596ba34bSRafael J. Wysocki { 559596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 560596ba34bSRafael J. Wysocki int ret; 561596ba34bSRafael J. Wysocki 562596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 563596ba34bSRafael J. Wysocki 564596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 565596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 566596ba34bSRafael J. Wysocki return -EINVAL; 567596ba34bSRafael J. Wysocki 568596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 569596ba34bSRafael J. Wysocki return 0; 570596ba34bSRafael J. Wysocki 571596ba34bSRafael J. Wysocki ret = pm_generic_freeze_noirq(dev); 572596ba34bSRafael J. Wysocki if (ret) 573596ba34bSRafael J. Wysocki return ret; 574596ba34bSRafael J. Wysocki 575596ba34bSRafael J. Wysocki if (genpd->stop_device) 576596ba34bSRafael J. Wysocki genpd->stop_device(dev); 577596ba34bSRafael J. Wysocki 578596ba34bSRafael J. Wysocki return 0; 579596ba34bSRafael J. Wysocki } 580596ba34bSRafael J. Wysocki 581596ba34bSRafael J. Wysocki /** 582596ba34bSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain. 583596ba34bSRafael J. Wysocki * @dev: Device to thaw. 584596ba34bSRafael J. Wysocki * 585596ba34bSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 586596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 587596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 588596ba34bSRafael J. Wysocki * devices. 589596ba34bSRafael J. Wysocki */ 590596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 591596ba34bSRafael J. Wysocki { 592596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 593596ba34bSRafael J. Wysocki 594596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 595596ba34bSRafael J. Wysocki 596596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 597596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 598596ba34bSRafael J. Wysocki return -EINVAL; 599596ba34bSRafael J. Wysocki 600596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 601596ba34bSRafael J. Wysocki return 0; 602596ba34bSRafael J. Wysocki 603596ba34bSRafael J. Wysocki if (genpd->start_device) 604596ba34bSRafael J. Wysocki genpd->start_device(dev); 605596ba34bSRafael J. Wysocki 606596ba34bSRafael J. Wysocki return pm_generic_thaw_noirq(dev); 607596ba34bSRafael J. Wysocki } 608596ba34bSRafael J. Wysocki 609596ba34bSRafael J. Wysocki /** 610596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 611596ba34bSRafael J. Wysocki * @dev: Device to thaw. 612596ba34bSRafael J. Wysocki * 613596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 614596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 615596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 616596ba34bSRafael J. Wysocki */ 617596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 618596ba34bSRafael J. Wysocki { 619596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 620596ba34bSRafael J. Wysocki 621596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 622596ba34bSRafael J. Wysocki 623596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 624596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 625596ba34bSRafael J. Wysocki return -EINVAL; 626596ba34bSRafael J. Wysocki 627596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 628596ba34bSRafael J. Wysocki } 629596ba34bSRafael J. Wysocki 630596ba34bSRafael J. Wysocki /** 631596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain. 632596ba34bSRafael J. Wysocki * @dev: Device to suspend. 633596ba34bSRafael J. Wysocki * 634596ba34bSRafael J. Wysocki * Power off a device under the assumption that its pm_domain field points to 635596ba34bSRafael J. Wysocki * the domain member of an object of type struct generic_pm_domain representing 636596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 637596ba34bSRafael J. Wysocki */ 638596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev) 639596ba34bSRafael J. Wysocki { 640596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 641596ba34bSRafael J. Wysocki 642596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 643596ba34bSRafael J. Wysocki 644596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 645596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 646596ba34bSRafael J. Wysocki return -EINVAL; 647596ba34bSRafael J. Wysocki 648596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev); 649596ba34bSRafael J. Wysocki } 650596ba34bSRafael J. Wysocki 651596ba34bSRafael J. Wysocki /** 652596ba34bSRafael J. Wysocki * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain. 653596ba34bSRafael J. Wysocki * @dev: Device to suspend. 654596ba34bSRafael J. Wysocki * 655596ba34bSRafael J. Wysocki * Carry out a late powering off of a device under the assumption that its 656596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 657596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 658596ba34bSRafael J. Wysocki */ 659596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev) 660596ba34bSRafael J. Wysocki { 661596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 662596ba34bSRafael J. Wysocki int ret; 663596ba34bSRafael J. Wysocki 664596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 665596ba34bSRafael J. Wysocki 666596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 667596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 668596ba34bSRafael J. Wysocki return -EINVAL; 669596ba34bSRafael J. Wysocki 670596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) 671596ba34bSRafael J. Wysocki return 0; 672596ba34bSRafael J. Wysocki 673596ba34bSRafael J. Wysocki ret = pm_generic_poweroff_noirq(dev); 674596ba34bSRafael J. Wysocki if (ret) 675596ba34bSRafael J. Wysocki return ret; 676596ba34bSRafael J. Wysocki 677d4f2d87aSRafael J. Wysocki if (device_may_wakeup(dev) 678d4f2d87aSRafael J. Wysocki && genpd->active_wakeup && genpd->active_wakeup(dev)) 679d4f2d87aSRafael J. Wysocki return 0; 680d4f2d87aSRafael J. Wysocki 681596ba34bSRafael J. Wysocki if (genpd->stop_device) 682596ba34bSRafael J. Wysocki genpd->stop_device(dev); 683596ba34bSRafael J. Wysocki 684596ba34bSRafael J. Wysocki /* 685596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 686596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 687596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 688596ba34bSRafael J. Wysocki */ 689596ba34bSRafael J. Wysocki genpd->suspended_count++; 690596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 691596ba34bSRafael J. Wysocki 692596ba34bSRafael J. Wysocki return 0; 693596ba34bSRafael J. Wysocki } 694596ba34bSRafael J. Wysocki 695596ba34bSRafael J. Wysocki /** 696596ba34bSRafael J. Wysocki * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain. 697596ba34bSRafael J. Wysocki * @dev: Device to resume. 698596ba34bSRafael J. Wysocki * 699596ba34bSRafael J. Wysocki * Carry out an early restore of a device under the assumption that its 700596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 701596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 702596ba34bSRafael J. Wysocki * devices. 703596ba34bSRafael J. Wysocki */ 704596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 705596ba34bSRafael J. Wysocki { 706596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 707596ba34bSRafael J. Wysocki 708596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 709596ba34bSRafael J. Wysocki 710596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 711596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 712596ba34bSRafael J. Wysocki return -EINVAL; 713596ba34bSRafael J. Wysocki 714596ba34bSRafael J. Wysocki /* 715596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 716596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 717596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 718596ba34bSRafael J. Wysocki */ 719596ba34bSRafael J. Wysocki genpd->power_is_off = true; 720596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 721596ba34bSRafael J. Wysocki /* 722596ba34bSRafael J. Wysocki * The boot kernel might put the domain into the power on state, 723596ba34bSRafael J. Wysocki * so make sure it really is powered off. 724596ba34bSRafael J. Wysocki */ 725596ba34bSRafael J. Wysocki if (genpd->power_off) 726596ba34bSRafael J. Wysocki genpd->power_off(genpd); 727596ba34bSRafael J. Wysocki return 0; 728596ba34bSRafael J. Wysocki } 729596ba34bSRafael J. Wysocki 730596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 731596ba34bSRafael J. Wysocki genpd->suspended_count--; 732596ba34bSRafael J. Wysocki if (genpd->start_device) 733596ba34bSRafael J. Wysocki genpd->start_device(dev); 734596ba34bSRafael J. Wysocki 735596ba34bSRafael J. Wysocki return pm_generic_restore_noirq(dev); 736596ba34bSRafael J. Wysocki } 737596ba34bSRafael J. Wysocki 738596ba34bSRafael J. Wysocki /** 739596ba34bSRafael J. Wysocki * pm_genpd_restore - Restore a device belonging to an I/O power domain. 740596ba34bSRafael J. Wysocki * @dev: Device to resume. 741596ba34bSRafael J. Wysocki * 742596ba34bSRafael J. Wysocki * Restore a device under the assumption that its pm_domain field points to the 743596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 744596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 745596ba34bSRafael J. Wysocki */ 746596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev) 747596ba34bSRafael J. Wysocki { 748596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 749596ba34bSRafael J. Wysocki 750596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 751596ba34bSRafael J. Wysocki 752596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 753596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 754596ba34bSRafael J. Wysocki return -EINVAL; 755596ba34bSRafael J. Wysocki 756596ba34bSRafael J. Wysocki return genpd->suspend_power_off ? 0 : pm_generic_restore(dev); 757596ba34bSRafael J. Wysocki } 758596ba34bSRafael J. Wysocki 759596ba34bSRafael J. Wysocki /** 760596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 761596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 762596ba34bSRafael J. Wysocki * 763596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 764596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 765596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 766596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 767596ba34bSRafael J. Wysocki */ 768596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 769596ba34bSRafael J. Wysocki { 770596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 771596ba34bSRafael J. Wysocki bool run_complete; 772596ba34bSRafael J. Wysocki 773596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 774596ba34bSRafael J. Wysocki 775596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 776596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 777596ba34bSRafael J. Wysocki return; 778596ba34bSRafael J. Wysocki 779596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 780596ba34bSRafael J. Wysocki 781596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 782596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 783596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 784596ba34bSRafael J. Wysocki 785596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 786596ba34bSRafael J. Wysocki 787596ba34bSRafael J. Wysocki if (run_complete) { 788596ba34bSRafael J. Wysocki pm_generic_complete(dev); 789596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 790596ba34bSRafael J. Wysocki } 791596ba34bSRafael J. Wysocki } 792596ba34bSRafael J. Wysocki 793596ba34bSRafael J. Wysocki #else 794596ba34bSRafael J. Wysocki 795596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 796596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 797596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 798596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 799596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 800596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 801596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 802596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 803596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 804596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq NULL 805596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff NULL 806596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 807596ba34bSRafael J. Wysocki #define pm_genpd_restore NULL 808596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 809596ba34bSRafael J. Wysocki 810596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 811596ba34bSRafael J. Wysocki 812f721889fSRafael J. Wysocki /** 813f721889fSRafael J. Wysocki * pm_genpd_add_device - Add a device to an I/O PM domain. 814f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 815f721889fSRafael J. Wysocki * @dev: Device to be added. 816f721889fSRafael J. Wysocki */ 817f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 818f721889fSRafael J. Wysocki { 819f721889fSRafael J. Wysocki struct dev_list_entry *dle; 820f721889fSRafael J. Wysocki int ret = 0; 821f721889fSRafael J. Wysocki 822f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 823f721889fSRafael J. Wysocki 824f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 825f721889fSRafael J. Wysocki return -EINVAL; 826f721889fSRafael J. Wysocki 827f721889fSRafael J. Wysocki mutex_lock(&genpd->lock); 828f721889fSRafael J. Wysocki 829f721889fSRafael J. Wysocki if (genpd->power_is_off) { 830f721889fSRafael J. Wysocki ret = -EINVAL; 831f721889fSRafael J. Wysocki goto out; 832f721889fSRafael J. Wysocki } 833f721889fSRafael J. Wysocki 834596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 835596ba34bSRafael J. Wysocki ret = -EAGAIN; 836596ba34bSRafael J. Wysocki goto out; 837596ba34bSRafael J. Wysocki } 838596ba34bSRafael J. Wysocki 839f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) 840f721889fSRafael J. Wysocki if (dle->dev == dev) { 841f721889fSRafael J. Wysocki ret = -EINVAL; 842f721889fSRafael J. Wysocki goto out; 843f721889fSRafael J. Wysocki } 844f721889fSRafael J. Wysocki 845f721889fSRafael J. Wysocki dle = kzalloc(sizeof(*dle), GFP_KERNEL); 846f721889fSRafael J. Wysocki if (!dle) { 847f721889fSRafael J. Wysocki ret = -ENOMEM; 848f721889fSRafael J. Wysocki goto out; 849f721889fSRafael J. Wysocki } 850f721889fSRafael J. Wysocki 851f721889fSRafael J. Wysocki dle->dev = dev; 852f721889fSRafael J. Wysocki dle->need_restore = false; 853f721889fSRafael J. Wysocki list_add_tail(&dle->node, &genpd->dev_list); 854596ba34bSRafael J. Wysocki genpd->device_count++; 855f721889fSRafael J. Wysocki 856f721889fSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 857f721889fSRafael J. Wysocki dev->pm_domain = &genpd->domain; 858f721889fSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 859f721889fSRafael J. Wysocki 860f721889fSRafael J. Wysocki out: 861f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 862f721889fSRafael J. Wysocki 863f721889fSRafael J. Wysocki return ret; 864f721889fSRafael J. Wysocki } 865f721889fSRafael J. Wysocki 866f721889fSRafael J. Wysocki /** 867f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 868f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 869f721889fSRafael J. Wysocki * @dev: Device to be removed. 870f721889fSRafael J. Wysocki */ 871f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 872f721889fSRafael J. Wysocki struct device *dev) 873f721889fSRafael J. Wysocki { 874f721889fSRafael J. Wysocki struct dev_list_entry *dle; 875f721889fSRafael J. Wysocki int ret = -EINVAL; 876f721889fSRafael J. Wysocki 877f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 878f721889fSRafael J. Wysocki 879f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 880f721889fSRafael J. Wysocki return -EINVAL; 881f721889fSRafael J. Wysocki 882f721889fSRafael J. Wysocki mutex_lock(&genpd->lock); 883f721889fSRafael J. Wysocki 884596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 885596ba34bSRafael J. Wysocki ret = -EAGAIN; 886596ba34bSRafael J. Wysocki goto out; 887596ba34bSRafael J. Wysocki } 888596ba34bSRafael J. Wysocki 889f721889fSRafael J. Wysocki list_for_each_entry(dle, &genpd->dev_list, node) { 890f721889fSRafael J. Wysocki if (dle->dev != dev) 891f721889fSRafael J. Wysocki continue; 892f721889fSRafael J. Wysocki 893f721889fSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 894f721889fSRafael J. Wysocki dev->pm_domain = NULL; 895f721889fSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 896f721889fSRafael J. Wysocki 897596ba34bSRafael J. Wysocki genpd->device_count--; 898f721889fSRafael J. Wysocki list_del(&dle->node); 899f721889fSRafael J. Wysocki kfree(dle); 900f721889fSRafael J. Wysocki 901f721889fSRafael J. Wysocki ret = 0; 902f721889fSRafael J. Wysocki break; 903f721889fSRafael J. Wysocki } 904f721889fSRafael J. Wysocki 905596ba34bSRafael J. Wysocki out: 906f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 907f721889fSRafael J. Wysocki 908f721889fSRafael J. Wysocki return ret; 909f721889fSRafael J. Wysocki } 910f721889fSRafael J. Wysocki 911f721889fSRafael J. Wysocki /** 912f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 913f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 914f721889fSRafael J. Wysocki * @new_subdomain: Subdomain to be added. 915f721889fSRafael J. Wysocki */ 916f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 917f721889fSRafael J. Wysocki struct generic_pm_domain *new_subdomain) 918f721889fSRafael J. Wysocki { 919f721889fSRafael J. Wysocki struct generic_pm_domain *subdomain; 920f721889fSRafael J. Wysocki int ret = 0; 921f721889fSRafael J. Wysocki 922f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain)) 923f721889fSRafael J. Wysocki return -EINVAL; 924f721889fSRafael J. Wysocki 925f721889fSRafael J. Wysocki mutex_lock(&genpd->lock); 926f721889fSRafael J. Wysocki 927f721889fSRafael J. Wysocki if (genpd->power_is_off && !new_subdomain->power_is_off) { 928f721889fSRafael J. Wysocki ret = -EINVAL; 929f721889fSRafael J. Wysocki goto out; 930f721889fSRafael J. Wysocki } 931f721889fSRafael J. Wysocki 932f721889fSRafael J. Wysocki list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 933f721889fSRafael J. Wysocki if (subdomain == new_subdomain) { 934f721889fSRafael J. Wysocki ret = -EINVAL; 935f721889fSRafael J. Wysocki goto out; 936f721889fSRafael J. Wysocki } 937f721889fSRafael J. Wysocki } 938f721889fSRafael J. Wysocki 939f721889fSRafael J. Wysocki mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING); 940f721889fSRafael J. Wysocki 941f721889fSRafael J. Wysocki list_add_tail(&new_subdomain->sd_node, &genpd->sd_list); 942f721889fSRafael J. Wysocki new_subdomain->parent = genpd; 943f721889fSRafael J. Wysocki if (!subdomain->power_is_off) 944f721889fSRafael J. Wysocki genpd->sd_count++; 945f721889fSRafael J. Wysocki 946f721889fSRafael J. Wysocki mutex_unlock(&new_subdomain->lock); 947f721889fSRafael J. Wysocki 948f721889fSRafael J. Wysocki out: 949f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 950f721889fSRafael J. Wysocki 951f721889fSRafael J. Wysocki return ret; 952f721889fSRafael J. Wysocki } 953f721889fSRafael J. Wysocki 954f721889fSRafael J. Wysocki /** 955f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 956f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 957f721889fSRafael J. Wysocki * @target: Subdomain to be removed. 958f721889fSRafael J. Wysocki */ 959f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 960f721889fSRafael J. Wysocki struct generic_pm_domain *target) 961f721889fSRafael J. Wysocki { 962f721889fSRafael J. Wysocki struct generic_pm_domain *subdomain; 963f721889fSRafael J. Wysocki int ret = -EINVAL; 964f721889fSRafael J. Wysocki 965f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target)) 966f721889fSRafael J. Wysocki return -EINVAL; 967f721889fSRafael J. Wysocki 968f721889fSRafael J. Wysocki mutex_lock(&genpd->lock); 969f721889fSRafael J. Wysocki 970f721889fSRafael J. Wysocki list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 971f721889fSRafael J. Wysocki if (subdomain != target) 972f721889fSRafael J. Wysocki continue; 973f721889fSRafael J. Wysocki 974f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 975f721889fSRafael J. Wysocki 976f721889fSRafael J. Wysocki list_del(&subdomain->sd_node); 977f721889fSRafael J. Wysocki subdomain->parent = NULL; 978f721889fSRafael J. Wysocki if (!subdomain->power_is_off) 979f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 980f721889fSRafael J. Wysocki 981f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 982f721889fSRafael J. Wysocki 983f721889fSRafael J. Wysocki ret = 0; 984f721889fSRafael J. Wysocki break; 985f721889fSRafael J. Wysocki } 986f721889fSRafael J. Wysocki 987f721889fSRafael J. Wysocki mutex_unlock(&genpd->lock); 988f721889fSRafael J. Wysocki 989f721889fSRafael J. Wysocki return ret; 990f721889fSRafael J. Wysocki } 991f721889fSRafael J. Wysocki 992f721889fSRafael J. Wysocki /** 993f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 994f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 995f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 996f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 997f721889fSRafael J. Wysocki */ 998f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 999f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1000f721889fSRafael J. Wysocki { 1001f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1002f721889fSRafael J. Wysocki return; 1003f721889fSRafael J. Wysocki 1004f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->sd_node); 1005f721889fSRafael J. Wysocki genpd->parent = NULL; 1006f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1007f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->sd_list); 1008f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1009f721889fSRafael J. Wysocki genpd->gov = gov; 1010f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1011f721889fSRafael J. Wysocki genpd->in_progress = 0; 1012f721889fSRafael J. Wysocki genpd->sd_count = 0; 1013f721889fSRafael J. Wysocki genpd->power_is_off = is_off; 1014596ba34bSRafael J. Wysocki genpd->device_count = 0; 1015596ba34bSRafael J. Wysocki genpd->suspended_count = 0; 1016f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1017f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1018f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1019596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1020596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 1021596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1022596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1023596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1024596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 1025596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1026596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1027596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1028596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_dev_poweroff; 1029596ba34bSRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq; 1030596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1031596ba34bSRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_restore; 1032596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1033f721889fSRafael J. Wysocki } 1034