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 993af5e93SGeert Uytterhoeven #include <linux/delay.h> 10f721889fSRafael J. Wysocki #include <linux/kernel.h> 11f721889fSRafael J. Wysocki #include <linux/io.h> 12aa42240aSTomasz Figa #include <linux/platform_device.h> 13f721889fSRafael J. Wysocki #include <linux/pm_runtime.h> 14f721889fSRafael J. Wysocki #include <linux/pm_domain.h> 156ff7bb0dSRafael J. Wysocki #include <linux/pm_qos.h> 16c11f6f5bSUlf Hansson #include <linux/pm_clock.h> 17f721889fSRafael J. Wysocki #include <linux/slab.h> 18f721889fSRafael J. Wysocki #include <linux/err.h> 1917b75ecaSRafael J. Wysocki #include <linux/sched.h> 2017b75ecaSRafael J. Wysocki #include <linux/suspend.h> 21d5e4cbfeSRafael J. Wysocki #include <linux/export.h> 22d5e4cbfeSRafael J. Wysocki 23aa8e54b5STomeu Vizoso #include "power.h" 24aa8e54b5STomeu Vizoso 2593af5e93SGeert Uytterhoeven #define GENPD_RETRY_MAX_MS 250 /* Approximate */ 2693af5e93SGeert Uytterhoeven 27d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 28d5e4cbfeSRafael J. Wysocki ({ \ 29d5e4cbfeSRafael J. Wysocki type (*__routine)(struct device *__d); \ 30d5e4cbfeSRafael J. Wysocki type __ret = (type)0; \ 31d5e4cbfeSRafael J. Wysocki \ 32d5e4cbfeSRafael J. Wysocki __routine = genpd->dev_ops.callback; \ 33d5e4cbfeSRafael J. Wysocki if (__routine) { \ 34d5e4cbfeSRafael J. Wysocki __ret = __routine(dev); \ 35d5e4cbfeSRafael J. Wysocki } \ 36d5e4cbfeSRafael J. Wysocki __ret; \ 37d5e4cbfeSRafael J. Wysocki }) 38f721889fSRafael J. Wysocki 395125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list); 405125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock); 415125bbf3SRafael J. Wysocki 4235241d12SLina Iyer struct genpd_lock_ops { 4335241d12SLina Iyer void (*lock)(struct generic_pm_domain *genpd); 4435241d12SLina Iyer void (*lock_nested)(struct generic_pm_domain *genpd, int depth); 4535241d12SLina Iyer int (*lock_interruptible)(struct generic_pm_domain *genpd); 4635241d12SLina Iyer void (*unlock)(struct generic_pm_domain *genpd); 4735241d12SLina Iyer }; 4835241d12SLina Iyer 4935241d12SLina Iyer static void genpd_lock_mtx(struct generic_pm_domain *genpd) 5035241d12SLina Iyer { 5135241d12SLina Iyer mutex_lock(&genpd->mlock); 5235241d12SLina Iyer } 5335241d12SLina Iyer 5435241d12SLina Iyer static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd, 5535241d12SLina Iyer int depth) 5635241d12SLina Iyer { 5735241d12SLina Iyer mutex_lock_nested(&genpd->mlock, depth); 5835241d12SLina Iyer } 5935241d12SLina Iyer 6035241d12SLina Iyer static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd) 6135241d12SLina Iyer { 6235241d12SLina Iyer return mutex_lock_interruptible(&genpd->mlock); 6335241d12SLina Iyer } 6435241d12SLina Iyer 6535241d12SLina Iyer static void genpd_unlock_mtx(struct generic_pm_domain *genpd) 6635241d12SLina Iyer { 6735241d12SLina Iyer return mutex_unlock(&genpd->mlock); 6835241d12SLina Iyer } 6935241d12SLina Iyer 7035241d12SLina Iyer static const struct genpd_lock_ops genpd_mtx_ops = { 7135241d12SLina Iyer .lock = genpd_lock_mtx, 7235241d12SLina Iyer .lock_nested = genpd_lock_nested_mtx, 7335241d12SLina Iyer .lock_interruptible = genpd_lock_interruptible_mtx, 7435241d12SLina Iyer .unlock = genpd_unlock_mtx, 7535241d12SLina Iyer }; 7635241d12SLina Iyer 77d716f479SLina Iyer static void genpd_lock_spin(struct generic_pm_domain *genpd) 78d716f479SLina Iyer __acquires(&genpd->slock) 79d716f479SLina Iyer { 80d716f479SLina Iyer unsigned long flags; 81d716f479SLina Iyer 82d716f479SLina Iyer spin_lock_irqsave(&genpd->slock, flags); 83d716f479SLina Iyer genpd->lock_flags = flags; 84d716f479SLina Iyer } 85d716f479SLina Iyer 86d716f479SLina Iyer static void genpd_lock_nested_spin(struct generic_pm_domain *genpd, 87d716f479SLina Iyer int depth) 88d716f479SLina Iyer __acquires(&genpd->slock) 89d716f479SLina Iyer { 90d716f479SLina Iyer unsigned long flags; 91d716f479SLina Iyer 92d716f479SLina Iyer spin_lock_irqsave_nested(&genpd->slock, flags, depth); 93d716f479SLina Iyer genpd->lock_flags = flags; 94d716f479SLina Iyer } 95d716f479SLina Iyer 96d716f479SLina Iyer static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd) 97d716f479SLina Iyer __acquires(&genpd->slock) 98d716f479SLina Iyer { 99d716f479SLina Iyer unsigned long flags; 100d716f479SLina Iyer 101d716f479SLina Iyer spin_lock_irqsave(&genpd->slock, flags); 102d716f479SLina Iyer genpd->lock_flags = flags; 103d716f479SLina Iyer return 0; 104d716f479SLina Iyer } 105d716f479SLina Iyer 106d716f479SLina Iyer static void genpd_unlock_spin(struct generic_pm_domain *genpd) 107d716f479SLina Iyer __releases(&genpd->slock) 108d716f479SLina Iyer { 109d716f479SLina Iyer spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags); 110d716f479SLina Iyer } 111d716f479SLina Iyer 112d716f479SLina Iyer static const struct genpd_lock_ops genpd_spin_ops = { 113d716f479SLina Iyer .lock = genpd_lock_spin, 114d716f479SLina Iyer .lock_nested = genpd_lock_nested_spin, 115d716f479SLina Iyer .lock_interruptible = genpd_lock_interruptible_spin, 116d716f479SLina Iyer .unlock = genpd_unlock_spin, 117d716f479SLina Iyer }; 118d716f479SLina Iyer 11935241d12SLina Iyer #define genpd_lock(p) p->lock_ops->lock(p) 12035241d12SLina Iyer #define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d) 12135241d12SLina Iyer #define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p) 12235241d12SLina Iyer #define genpd_unlock(p) p->lock_ops->unlock(p) 12335241d12SLina Iyer 124d716f479SLina Iyer #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) 125d716f479SLina Iyer 126d716f479SLina Iyer static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, 127d716f479SLina Iyer struct generic_pm_domain *genpd) 128d716f479SLina Iyer { 129d716f479SLina Iyer bool ret; 130d716f479SLina Iyer 131d716f479SLina Iyer ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); 132d716f479SLina Iyer 133d716f479SLina Iyer /* Warn once for each IRQ safe dev in no sleep domain */ 134d716f479SLina Iyer if (ret) 135d716f479SLina Iyer dev_warn_once(dev, "PM domain %s will not be powered off\n", 136d716f479SLina Iyer genpd->name); 137d716f479SLina Iyer 138d716f479SLina Iyer return ret; 139d716f479SLina Iyer } 140d716f479SLina Iyer 141446d999cSRussell King /* 142446d999cSRussell King * Get the generic PM domain for a particular struct device. 143446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 144446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 145446d999cSRussell King * Any failure results in NULL being returned. 146446d999cSRussell King */ 147f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_lookup_dev(struct device *dev) 148446d999cSRussell King { 149446d999cSRussell King struct generic_pm_domain *genpd = NULL, *gpd; 150446d999cSRussell King 151446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 152446d999cSRussell King return NULL; 153446d999cSRussell King 154446d999cSRussell King mutex_lock(&gpd_list_lock); 155446d999cSRussell King list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 156446d999cSRussell King if (&gpd->domain == dev->pm_domain) { 157446d999cSRussell King genpd = gpd; 158446d999cSRussell King break; 159446d999cSRussell King } 160446d999cSRussell King } 161446d999cSRussell King mutex_unlock(&gpd_list_lock); 162446d999cSRussell King 163446d999cSRussell King return genpd; 164446d999cSRussell King } 165446d999cSRussell King 166446d999cSRussell King /* 167446d999cSRussell King * This should only be used where we are certain that the pm_domain 168446d999cSRussell King * attached to the device is a genpd domain. 169446d999cSRussell King */ 170446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 1715248051bSRafael J. Wysocki { 1725248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 1735248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 1745248051bSRafael J. Wysocki 175596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 1765248051bSRafael J. Wysocki } 177f721889fSRafael J. Wysocki 1782b1d88cdSUlf Hansson static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) 179d5e4cbfeSRafael J. Wysocki { 18051cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 181d5e4cbfeSRafael J. Wysocki } 182d5e4cbfeSRafael J. Wysocki 1832b1d88cdSUlf Hansson static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) 184d5e4cbfeSRafael J. Wysocki { 185ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 186d5e4cbfeSRafael J. Wysocki } 187d5e4cbfeSRafael J. Wysocki 188c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 189f721889fSRafael J. Wysocki { 190c4bb3160SRafael J. Wysocki bool ret = false; 191c4bb3160SRafael J. Wysocki 192c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 193c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 194c4bb3160SRafael J. Wysocki 195c4bb3160SRafael J. Wysocki return ret; 196c4bb3160SRafael J. Wysocki } 197c4bb3160SRafael J. Wysocki 198c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 199c4bb3160SRafael J. Wysocki { 200c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 2014e857c58SPeter Zijlstra smp_mb__after_atomic(); 202f721889fSRafael J. Wysocki } 203f721889fSRafael J. Wysocki 20486e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 205c8f0ea45SGeert Uytterhoeven { 206fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 207c8f0ea45SGeert Uytterhoeven ktime_t time_start; 208c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 209c8f0ea45SGeert Uytterhoeven int ret; 210c8f0ea45SGeert Uytterhoeven 211c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 212c8f0ea45SGeert Uytterhoeven return 0; 213c8f0ea45SGeert Uytterhoeven 214a4630c61SGeert Uytterhoeven if (!timed) 215a4630c61SGeert Uytterhoeven return genpd->power_on(genpd); 216a4630c61SGeert Uytterhoeven 217c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 218c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 219c8f0ea45SGeert Uytterhoeven if (ret) 220c8f0ea45SGeert Uytterhoeven return ret; 221c8f0ea45SGeert Uytterhoeven 222c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 223fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 224c8f0ea45SGeert Uytterhoeven return ret; 225c8f0ea45SGeert Uytterhoeven 226fc5cbf0cSAxel Haslam genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 227c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 2286d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 229c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 230c8f0ea45SGeert Uytterhoeven 231c8f0ea45SGeert Uytterhoeven return ret; 232c8f0ea45SGeert Uytterhoeven } 233c8f0ea45SGeert Uytterhoeven 23486e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) 235c8f0ea45SGeert Uytterhoeven { 236fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 237c8f0ea45SGeert Uytterhoeven ktime_t time_start; 238c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 239c8f0ea45SGeert Uytterhoeven int ret; 240c8f0ea45SGeert Uytterhoeven 241c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 242c8f0ea45SGeert Uytterhoeven return 0; 243c8f0ea45SGeert Uytterhoeven 244a4630c61SGeert Uytterhoeven if (!timed) 245a4630c61SGeert Uytterhoeven return genpd->power_off(genpd); 246a4630c61SGeert Uytterhoeven 247c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 248c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 249c8f0ea45SGeert Uytterhoeven if (ret == -EBUSY) 250c8f0ea45SGeert Uytterhoeven return ret; 251c8f0ea45SGeert Uytterhoeven 252c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 253fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 254c8f0ea45SGeert Uytterhoeven return ret; 255c8f0ea45SGeert Uytterhoeven 256fc5cbf0cSAxel Haslam genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 257c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 2586d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 259c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 260c8f0ea45SGeert Uytterhoeven 261c8f0ea45SGeert Uytterhoeven return ret; 262c8f0ea45SGeert Uytterhoeven } 263c8f0ea45SGeert Uytterhoeven 264f721889fSRafael J. Wysocki /** 26586e12eacSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_power_off(). 266a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 26729e47e21SUlf Hansson * 26886e12eacSUlf Hansson * Queue up the execution of genpd_power_off() unless it's already been done 26929e47e21SUlf Hansson * before. 27029e47e21SUlf Hansson */ 27129e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 27229e47e21SUlf Hansson { 27329e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 27429e47e21SUlf Hansson } 27529e47e21SUlf Hansson 27629e47e21SUlf Hansson /** 27786e12eacSUlf Hansson * genpd_power_on - Restore power to a given PM domain and its masters. 2785248051bSRafael J. Wysocki * @genpd: PM domain to power up. 2790106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 2805248051bSRafael J. Wysocki * 2815063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 2825248051bSRafael J. Wysocki * resume a device belonging to it. 2835248051bSRafael J. Wysocki */ 28486e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) 2855248051bSRafael J. Wysocki { 2865063ce15SRafael J. Wysocki struct gpd_link *link; 2875248051bSRafael J. Wysocki int ret = 0; 2885248051bSRafael J. Wysocki 28939dd0f23SUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 2903f241775SRafael J. Wysocki return 0; 2915248051bSRafael J. Wysocki 2925063ce15SRafael J. Wysocki /* 2935063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 2945063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 2955063ce15SRafael J. Wysocki * with it. 2965063ce15SRafael J. Wysocki */ 2975063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 2980106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 2995248051bSRafael J. Wysocki 3000106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 3010106ef51SMarek Szyprowski 30235241d12SLina Iyer genpd_lock_nested(master, depth + 1); 30386e12eacSUlf Hansson ret = genpd_power_on(master, depth + 1); 30435241d12SLina Iyer genpd_unlock(master); 3050106ef51SMarek Szyprowski 3065063ce15SRafael J. Wysocki if (ret) { 3070106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 3089e08cf42SRafael J. Wysocki goto err; 3095248051bSRafael J. Wysocki } 3105063ce15SRafael J. Wysocki } 3115248051bSRafael J. Wysocki 31286e12eacSUlf Hansson ret = _genpd_power_on(genpd, true); 3139e08cf42SRafael J. Wysocki if (ret) 3149e08cf42SRafael J. Wysocki goto err; 3150140d8bdSRafael J. Wysocki 316ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 3173f241775SRafael J. Wysocki return 0; 3189e08cf42SRafael J. Wysocki 3199e08cf42SRafael J. Wysocki err: 32029e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 32129e47e21SUlf Hansson &genpd->slave_links, 32229e47e21SUlf Hansson slave_node) { 3235063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 32429e47e21SUlf Hansson genpd_queue_power_off_work(link->master); 32529e47e21SUlf Hansson } 3269e08cf42SRafael J. Wysocki 3273f241775SRafael J. Wysocki return ret; 3283f241775SRafael J. Wysocki } 3293f241775SRafael J. Wysocki 3306ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 3316ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 3326ff7bb0dSRafael J. Wysocki { 3336ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 3346ff7bb0dSRafael J. Wysocki struct device *dev; 3356ff7bb0dSRafael J. Wysocki 3366ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 3376ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 3386ff7bb0dSRafael J. Wysocki 3396ff7bb0dSRafael J. Wysocki for (;;) { 3406ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 3416ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 3426ff7bb0dSRafael J. Wysocki 3436ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 3446ff7bb0dSRafael J. Wysocki 3456ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 3466ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 3471d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 3486ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 3496ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 3506ff7bb0dSRafael J. Wysocki } else { 3516ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 3526ff7bb0dSRafael J. Wysocki } 3536ff7bb0dSRafael J. Wysocki 3546ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 3556ff7bb0dSRafael J. Wysocki 3566ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 35735241d12SLina Iyer genpd_lock(genpd); 3586ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 35935241d12SLina Iyer genpd_unlock(genpd); 3606ff7bb0dSRafael J. Wysocki } 3616ff7bb0dSRafael J. Wysocki 3626ff7bb0dSRafael J. Wysocki dev = dev->parent; 3636ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 3646ff7bb0dSRafael J. Wysocki break; 3656ff7bb0dSRafael J. Wysocki } 3666ff7bb0dSRafael J. Wysocki 3676ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 3686ff7bb0dSRafael J. Wysocki } 3696ff7bb0dSRafael J. Wysocki 3705248051bSRafael J. Wysocki /** 37186e12eacSUlf Hansson * genpd_power_off - Remove power from a given PM domain. 372f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 373f96b3c4fSUlf Hansson * @is_async: PM domain is powered down from a scheduled work 374f721889fSRafael J. Wysocki * 375f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 376ba2bbfbfSUlf Hansson * have been powered down, remove power from @genpd. 377f721889fSRafael J. Wysocki */ 37886e12eacSUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool is_async) 379f721889fSRafael J. Wysocki { 3804605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 3815063ce15SRafael J. Wysocki struct gpd_link *link; 382ba2bbfbfSUlf Hansson unsigned int not_suspended = 0; 383f721889fSRafael J. Wysocki 384c6d22b37SRafael J. Wysocki /* 385c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 386c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 387ba2bbfbfSUlf Hansson * (2) System suspend is in progress. 388c6d22b37SRafael J. Wysocki */ 3893f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 390ba2bbfbfSUlf Hansson || genpd->prepared_count > 0) 391f721889fSRafael J. Wysocki return 0; 392f721889fSRafael J. Wysocki 393c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 394f721889fSRafael J. Wysocki return -EBUSY; 395f721889fSRafael J. Wysocki 39634b1f762SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) { 39734b1f762SRafael J. Wysocki enum pm_qos_flags_status stat; 39834b1f762SRafael J. Wysocki 39934b1f762SRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, 40034b1f762SRafael J. Wysocki PM_QOS_FLAG_NO_POWER_OFF 40134b1f762SRafael J. Wysocki | PM_QOS_FLAG_REMOTE_WAKEUP); 40234b1f762SRafael J. Wysocki if (stat > PM_QOS_FLAGS_NONE) 40334b1f762SRafael J. Wysocki return -EBUSY; 40434b1f762SRafael J. Wysocki 405d716f479SLina Iyer /* 406d716f479SLina Iyer * Do not allow PM domain to be powered off, when an IRQ safe 407d716f479SLina Iyer * device is part of a non-IRQ safe domain. 408d716f479SLina Iyer */ 409d716f479SLina Iyer if (!pm_runtime_suspended(pdd->dev) || 410d716f479SLina Iyer irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) 411f721889fSRafael J. Wysocki not_suspended++; 41234b1f762SRafael J. Wysocki } 413f721889fSRafael J. Wysocki 414f96b3c4fSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && is_async)) 415f721889fSRafael J. Wysocki return -EBUSY; 416f721889fSRafael J. Wysocki 417f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 418f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 419f721889fSRafael J. Wysocki return -EAGAIN; 420f721889fSRafael J. Wysocki } 421f721889fSRafael J. Wysocki 4223c07cbc4SRafael J. Wysocki if (genpd->power_off) { 423ba2bbfbfSUlf Hansson int ret; 424ba2bbfbfSUlf Hansson 425ba2bbfbfSUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 426ba2bbfbfSUlf Hansson return -EBUSY; 42717b75ecaSRafael J. Wysocki 4283c07cbc4SRafael J. Wysocki /* 4295063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 43086e12eacSUlf Hansson * managed to call genpd_power_on() for the master yet after 43186e12eacSUlf Hansson * incrementing it. In that case genpd_power_on() will wait 4323c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 43386e12eacSUlf Hansson * the genpd_power_on() restore power for us (this shouldn't 4343c07cbc4SRafael J. Wysocki * happen very often). 4353c07cbc4SRafael J. Wysocki */ 43686e12eacSUlf Hansson ret = _genpd_power_off(genpd, true); 437ba2bbfbfSUlf Hansson if (ret) 438ba2bbfbfSUlf Hansson return ret; 439d2805402SRafael J. Wysocki } 440f721889fSRafael J. Wysocki 44117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 442221e9b58SRafael J. Wysocki 4435063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 4445063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 4455063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 4465063ce15SRafael J. Wysocki } 44717b75ecaSRafael J. Wysocki 448ba2bbfbfSUlf Hansson return 0; 449f721889fSRafael J. Wysocki } 450f721889fSRafael J. Wysocki 451f721889fSRafael J. Wysocki /** 452f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 453f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 454f721889fSRafael J. Wysocki */ 455f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 456f721889fSRafael J. Wysocki { 457f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 458f721889fSRafael J. Wysocki 459f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 460f721889fSRafael J. Wysocki 46135241d12SLina Iyer genpd_lock(genpd); 46286e12eacSUlf Hansson genpd_power_off(genpd, true); 46335241d12SLina Iyer genpd_unlock(genpd); 464f721889fSRafael J. Wysocki } 465f721889fSRafael J. Wysocki 466f721889fSRafael J. Wysocki /** 46754eeddbfSUlf Hansson * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 46854eeddbfSUlf Hansson * @dev: Device to handle. 46954eeddbfSUlf Hansson */ 47054eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev) 47154eeddbfSUlf Hansson { 47254eeddbfSUlf Hansson int (*cb)(struct device *__dev); 47354eeddbfSUlf Hansson 47454eeddbfSUlf Hansson if (dev->type && dev->type->pm) 47554eeddbfSUlf Hansson cb = dev->type->pm->runtime_suspend; 47654eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 47754eeddbfSUlf Hansson cb = dev->class->pm->runtime_suspend; 47854eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 47954eeddbfSUlf Hansson cb = dev->bus->pm->runtime_suspend; 48054eeddbfSUlf Hansson else 48154eeddbfSUlf Hansson cb = NULL; 48254eeddbfSUlf Hansson 48354eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 48454eeddbfSUlf Hansson cb = dev->driver->pm->runtime_suspend; 48554eeddbfSUlf Hansson 48654eeddbfSUlf Hansson return cb ? cb(dev) : 0; 48754eeddbfSUlf Hansson } 48854eeddbfSUlf Hansson 48954eeddbfSUlf Hansson /** 49054eeddbfSUlf Hansson * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 49154eeddbfSUlf Hansson * @dev: Device to handle. 49254eeddbfSUlf Hansson */ 49354eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev) 49454eeddbfSUlf Hansson { 49554eeddbfSUlf Hansson int (*cb)(struct device *__dev); 49654eeddbfSUlf Hansson 49754eeddbfSUlf Hansson if (dev->type && dev->type->pm) 49854eeddbfSUlf Hansson cb = dev->type->pm->runtime_resume; 49954eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 50054eeddbfSUlf Hansson cb = dev->class->pm->runtime_resume; 50154eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 50254eeddbfSUlf Hansson cb = dev->bus->pm->runtime_resume; 50354eeddbfSUlf Hansson else 50454eeddbfSUlf Hansson cb = NULL; 50554eeddbfSUlf Hansson 50654eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 50754eeddbfSUlf Hansson cb = dev->driver->pm->runtime_resume; 50854eeddbfSUlf Hansson 50954eeddbfSUlf Hansson return cb ? cb(dev) : 0; 51054eeddbfSUlf Hansson } 51154eeddbfSUlf Hansson 51254eeddbfSUlf Hansson /** 513795bd2e7SUlf Hansson * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 514f721889fSRafael J. Wysocki * @dev: Device to suspend. 515f721889fSRafael J. Wysocki * 516f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 517f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 518f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 519f721889fSRafael J. Wysocki */ 520795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev) 521f721889fSRafael J. Wysocki { 522f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 5239df3921eSUlf Hansson bool (*suspend_ok)(struct device *__dev); 5242b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 525ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 5262b1d88cdSUlf Hansson ktime_t time_start; 5272b1d88cdSUlf Hansson s64 elapsed_ns; 528d5e4cbfeSRafael J. Wysocki int ret; 529f721889fSRafael J. Wysocki 530f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 531f721889fSRafael J. Wysocki 5325248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5335248051bSRafael J. Wysocki if (IS_ERR(genpd)) 534f721889fSRafael J. Wysocki return -EINVAL; 535f721889fSRafael J. Wysocki 536ffe12855SUlf Hansson /* 537ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 538ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 539ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 540ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 541ffe12855SUlf Hansson */ 5429df3921eSUlf Hansson suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 5439df3921eSUlf Hansson if (runtime_pm && suspend_ok && !suspend_ok(dev)) 544b02c999aSRafael J. Wysocki return -EBUSY; 545b02c999aSRafael J. Wysocki 5462b1d88cdSUlf Hansson /* Measure suspend latency. */ 547d33d5a6cSLinus Torvalds time_start = 0; 548ffe12855SUlf Hansson if (runtime_pm) 5492b1d88cdSUlf Hansson time_start = ktime_get(); 5502b1d88cdSUlf Hansson 55154eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 552f721889fSRafael J. Wysocki if (ret) 55317b75ecaSRafael J. Wysocki return ret; 55417b75ecaSRafael J. Wysocki 5552b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 556ba2bbfbfSUlf Hansson if (ret) { 55754eeddbfSUlf Hansson __genpd_runtime_resume(dev); 558ba2bbfbfSUlf Hansson return ret; 559ba2bbfbfSUlf Hansson } 560ba2bbfbfSUlf Hansson 5612b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 562ffe12855SUlf Hansson if (runtime_pm) { 5632b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5642b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 5652b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 5662b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 5672b1d88cdSUlf Hansson elapsed_ns); 5682b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5692b1d88cdSUlf Hansson td->constraint_changed = true; 5702b1d88cdSUlf Hansson } 571ffe12855SUlf Hansson } 5722b1d88cdSUlf Hansson 5730aa2a221SRafael J. Wysocki /* 574d716f479SLina Iyer * If power.irq_safe is set, this routine may be run with 575d716f479SLina Iyer * IRQs disabled, so suspend only if the PM domain also is irq_safe. 5760aa2a221SRafael J. Wysocki */ 577d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) 5780aa2a221SRafael J. Wysocki return 0; 5790aa2a221SRafael J. Wysocki 58035241d12SLina Iyer genpd_lock(genpd); 58186e12eacSUlf Hansson genpd_power_off(genpd, false); 58235241d12SLina Iyer genpd_unlock(genpd); 583f721889fSRafael J. Wysocki 584f721889fSRafael J. Wysocki return 0; 585f721889fSRafael J. Wysocki } 586f721889fSRafael J. Wysocki 587f721889fSRafael J. Wysocki /** 588795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 589f721889fSRafael J. Wysocki * @dev: Device to resume. 590f721889fSRafael J. Wysocki * 591f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 592f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 593f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 594f721889fSRafael J. Wysocki */ 595795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 596f721889fSRafael J. Wysocki { 597f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 5982b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 599ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 6002b1d88cdSUlf Hansson ktime_t time_start; 6012b1d88cdSUlf Hansson s64 elapsed_ns; 602f721889fSRafael J. Wysocki int ret; 603ba2bbfbfSUlf Hansson bool timed = true; 604f721889fSRafael J. Wysocki 605f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 606f721889fSRafael J. Wysocki 6075248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6085248051bSRafael J. Wysocki if (IS_ERR(genpd)) 609f721889fSRafael J. Wysocki return -EINVAL; 610f721889fSRafael J. Wysocki 611d716f479SLina Iyer /* 612d716f479SLina Iyer * As we don't power off a non IRQ safe domain, which holds 613d716f479SLina Iyer * an IRQ safe device, we don't need to restore power to it. 614d716f479SLina Iyer */ 615d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) { 616ba2bbfbfSUlf Hansson timed = false; 617ba2bbfbfSUlf Hansson goto out; 618ba2bbfbfSUlf Hansson } 6190aa2a221SRafael J. Wysocki 62035241d12SLina Iyer genpd_lock(genpd); 62186e12eacSUlf Hansson ret = genpd_power_on(genpd, 0); 62235241d12SLina Iyer genpd_unlock(genpd); 623ba2bbfbfSUlf Hansson 624ba2bbfbfSUlf Hansson if (ret) 6253f241775SRafael J. Wysocki return ret; 626c6d22b37SRafael J. Wysocki 627ba2bbfbfSUlf Hansson out: 6282b1d88cdSUlf Hansson /* Measure resume latency. */ 629ab51e6baSAugusto Mecking Caringi time_start = 0; 630ffe12855SUlf Hansson if (timed && runtime_pm) 6312b1d88cdSUlf Hansson time_start = ktime_get(); 6322b1d88cdSUlf Hansson 633076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 634076395caSLaurent Pinchart if (ret) 635076395caSLaurent Pinchart goto err_poweroff; 636076395caSLaurent Pinchart 63754eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 638076395caSLaurent Pinchart if (ret) 639076395caSLaurent Pinchart goto err_stop; 6402b1d88cdSUlf Hansson 6412b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 642ffe12855SUlf Hansson if (timed && runtime_pm) { 6432b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 6442b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 6452b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 6462b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 6472b1d88cdSUlf Hansson elapsed_ns); 6482b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 6492b1d88cdSUlf Hansson td->constraint_changed = true; 6502b1d88cdSUlf Hansson } 6512b1d88cdSUlf Hansson } 65217b75ecaSRafael J. Wysocki 653f721889fSRafael J. Wysocki return 0; 654076395caSLaurent Pinchart 655076395caSLaurent Pinchart err_stop: 656076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 657076395caSLaurent Pinchart err_poweroff: 658d716f479SLina Iyer if (!pm_runtime_is_irq_safe(dev) || 659d716f479SLina Iyer (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { 66035241d12SLina Iyer genpd_lock(genpd); 66186e12eacSUlf Hansson genpd_power_off(genpd, 0); 66235241d12SLina Iyer genpd_unlock(genpd); 663076395caSLaurent Pinchart } 664076395caSLaurent Pinchart 665076395caSLaurent Pinchart return ret; 666f721889fSRafael J. Wysocki } 667f721889fSRafael J. Wysocki 66839ac5ba5STushar Behera static bool pd_ignore_unused; 66939ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 67039ac5ba5STushar Behera { 67139ac5ba5STushar Behera pd_ignore_unused = true; 67239ac5ba5STushar Behera return 1; 67339ac5ba5STushar Behera } 67439ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 67539ac5ba5STushar Behera 67617f2ae7fSRafael J. Wysocki /** 67786e12eacSUlf Hansson * genpd_power_off_unused - Power off all PM domains with no devices in use. 67817f2ae7fSRafael J. Wysocki */ 67986e12eacSUlf Hansson static int __init genpd_power_off_unused(void) 68017f2ae7fSRafael J. Wysocki { 68117f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 68217f2ae7fSRafael J. Wysocki 68339ac5ba5STushar Behera if (pd_ignore_unused) { 68439ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 685bb4b72fcSUlf Hansson return 0; 68639ac5ba5STushar Behera } 68739ac5ba5STushar Behera 68817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 68917f2ae7fSRafael J. Wysocki 69017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 69117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 69217f2ae7fSRafael J. Wysocki 69317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 69417f2ae7fSRafael J. Wysocki 6952fe71dcdSUlf Hansson return 0; 6962fe71dcdSUlf Hansson } 69786e12eacSUlf Hansson late_initcall(genpd_power_off_unused); 6982fe71dcdSUlf Hansson 6990159ec67SJon Hunter #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) 700596ba34bSRafael J. Wysocki 70177f827deSRafael J. Wysocki /** 70277f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 70377f827deSRafael J. Wysocki * @genpd: PM domain to check. 70477f827deSRafael J. Wysocki */ 705895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 70677f827deSRafael J. Wysocki { 707895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 70877f827deSRafael J. Wysocki 70977f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 71077f827deSRafael J. Wysocki return false; 71177f827deSRafael J. Wysocki 71277f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 71377f827deSRafael J. Wysocki if (gpd == genpd) 71477f827deSRafael J. Wysocki return true; 71577f827deSRafael J. Wysocki 71677f827deSRafael J. Wysocki return false; 71777f827deSRafael J. Wysocki } 71877f827deSRafael J. Wysocki 7190159ec67SJon Hunter #endif 7200159ec67SJon Hunter 7210159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 7220159ec67SJon Hunter 723d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 724d5e4cbfeSRafael J. Wysocki struct device *dev) 725d5e4cbfeSRafael J. Wysocki { 726d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 727d5e4cbfeSRafael J. Wysocki } 728d5e4cbfeSRafael J. Wysocki 729596ba34bSRafael J. Wysocki /** 73086e12eacSUlf Hansson * genpd_sync_power_off - Synchronously power off a PM domain and its masters. 731596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 732596ba34bSRafael J. Wysocki * 733596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7345063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 735596ba34bSRafael J. Wysocki * 73677f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 73777f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 73877f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 73977f827deSRafael J. Wysocki * parallel). 740596ba34bSRafael J. Wysocki */ 74186e12eacSUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd) 742596ba34bSRafael J. Wysocki { 7435063ce15SRafael J. Wysocki struct gpd_link *link; 744596ba34bSRafael J. Wysocki 74517b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 746596ba34bSRafael J. Wysocki return; 747596ba34bSRafael J. Wysocki 748c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 749c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 750596ba34bSRafael J. Wysocki return; 751596ba34bSRafael J. Wysocki 752fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 753fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 75486e12eacSUlf Hansson _genpd_power_off(genpd, false); 755596ba34bSRafael J. Wysocki 75617b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7575063ce15SRafael J. Wysocki 7585063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7595063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 76086e12eacSUlf Hansson genpd_sync_power_off(link->master); 761596ba34bSRafael J. Wysocki } 762596ba34bSRafael J. Wysocki } 763596ba34bSRafael J. Wysocki 764596ba34bSRafael J. Wysocki /** 76586e12eacSUlf Hansson * genpd_sync_power_on - Synchronously power on a PM domain and its masters. 766802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 767802d8b49SRafael J. Wysocki * 76877f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 76977f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 77077f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 77177f827deSRafael J. Wysocki * parallel). 772802d8b49SRafael J. Wysocki */ 77386e12eacSUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd) 774802d8b49SRafael J. Wysocki { 775802d8b49SRafael J. Wysocki struct gpd_link *link; 776802d8b49SRafael J. Wysocki 777ba2bbfbfSUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 778802d8b49SRafael J. Wysocki return; 779802d8b49SRafael J. Wysocki 780802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 78186e12eacSUlf Hansson genpd_sync_power_on(link->master); 782802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 783802d8b49SRafael J. Wysocki } 784802d8b49SRafael J. Wysocki 78586e12eacSUlf Hansson _genpd_power_on(genpd, false); 786802d8b49SRafael J. Wysocki 787802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 788802d8b49SRafael J. Wysocki } 789802d8b49SRafael J. Wysocki 790802d8b49SRafael J. Wysocki /** 7914ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 7924ecd6e65SRafael J. Wysocki * @dev: Device to check. 7934ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 7944ecd6e65SRafael J. Wysocki * 7954ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 7964ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 7974ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 7984ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 7994ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 8004ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 8014ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8024ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8034ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8044ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8054ecd6e65SRafael J. Wysocki */ 8064ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 8074ecd6e65SRafael J. Wysocki { 8084ecd6e65SRafael J. Wysocki bool active_wakeup; 8094ecd6e65SRafael J. Wysocki 8104ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8114ecd6e65SRafael J. Wysocki return false; 8124ecd6e65SRafael J. Wysocki 813d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8144ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8154ecd6e65SRafael J. Wysocki } 8164ecd6e65SRafael J. Wysocki 8174ecd6e65SRafael J. Wysocki /** 818596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 819596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 820596ba34bSRafael J. Wysocki * 821596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 822596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 823596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 824596ba34bSRafael J. Wysocki * consisting of I/O devices. 825596ba34bSRafael J. Wysocki */ 826596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 827596ba34bSRafael J. Wysocki { 828596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 829b6c10c84SRafael J. Wysocki int ret; 830596ba34bSRafael J. Wysocki 831596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 832596ba34bSRafael J. Wysocki 833596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 834596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 835596ba34bSRafael J. Wysocki return -EINVAL; 836596ba34bSRafael J. Wysocki 83717b75ecaSRafael J. Wysocki /* 83817b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 83917b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 84017b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 84117b75ecaSRafael J. Wysocki */ 8424ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8434ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8444ecd6e65SRafael J. Wysocki 84535241d12SLina Iyer genpd_lock(genpd); 846596ba34bSRafael J. Wysocki 84739dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 84865533bbfSRafael J. Wysocki genpd->suspended_count = 0; 84917b75ecaSRafael J. Wysocki 85035241d12SLina Iyer genpd_unlock(genpd); 851596ba34bSRafael J. Wysocki 852b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 853b6c10c84SRafael J. Wysocki if (ret) { 85435241d12SLina Iyer genpd_lock(genpd); 855b6c10c84SRafael J. Wysocki 85639dd0f23SUlf Hansson genpd->prepared_count--; 857b6c10c84SRafael J. Wysocki 85835241d12SLina Iyer genpd_unlock(genpd); 859b6c10c84SRafael J. Wysocki } 86017b75ecaSRafael J. Wysocki 861b6c10c84SRafael J. Wysocki return ret; 862596ba34bSRafael J. Wysocki } 863596ba34bSRafael J. Wysocki 864596ba34bSRafael J. Wysocki /** 8650496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 8660496c8aeSRafael J. Wysocki * @dev: Device to suspend. 8670496c8aeSRafael J. Wysocki * 8680496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 8690496c8aeSRafael J. Wysocki * been stopped. 8700496c8aeSRafael J. Wysocki */ 8710496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 8720496c8aeSRafael J. Wysocki { 8730496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 874122a2237SUlf Hansson int ret; 875596ba34bSRafael J. Wysocki 8760496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8770496c8aeSRafael J. Wysocki 8780496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 8790496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 8800496c8aeSRafael J. Wysocki return -EINVAL; 8810496c8aeSRafael J. Wysocki 88239dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 883d4f2d87aSRafael J. Wysocki return 0; 884d4f2d87aSRafael J. Wysocki 885122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) { 886122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 887122a2237SUlf Hansson if (ret) 888122a2237SUlf Hansson return ret; 889122a2237SUlf Hansson } 890122a2237SUlf Hansson 891596ba34bSRafael J. Wysocki /* 892596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 893596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 894596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 895596ba34bSRafael J. Wysocki */ 896596ba34bSRafael J. Wysocki genpd->suspended_count++; 89786e12eacSUlf Hansson genpd_sync_power_off(genpd); 898596ba34bSRafael J. Wysocki 899596ba34bSRafael J. Wysocki return 0; 900596ba34bSRafael J. Wysocki } 901596ba34bSRafael J. Wysocki 902596ba34bSRafael J. Wysocki /** 9030496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 904596ba34bSRafael J. Wysocki * @dev: Device to resume. 905596ba34bSRafael J. Wysocki * 9060496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 907596ba34bSRafael J. Wysocki */ 908596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 909596ba34bSRafael J. Wysocki { 910596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 911122a2237SUlf Hansson int ret = 0; 912596ba34bSRafael J. Wysocki 913596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 914596ba34bSRafael J. Wysocki 915596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 916596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 917596ba34bSRafael J. Wysocki return -EINVAL; 918596ba34bSRafael J. Wysocki 91939dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 920596ba34bSRafael J. Wysocki return 0; 921596ba34bSRafael J. Wysocki 922596ba34bSRafael J. Wysocki /* 923596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 924596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 925596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 926596ba34bSRafael J. Wysocki */ 92786e12eacSUlf Hansson genpd_sync_power_on(genpd); 928596ba34bSRafael J. Wysocki genpd->suspended_count--; 929596ba34bSRafael J. Wysocki 930122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 931122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 932122a2237SUlf Hansson 933122a2237SUlf Hansson return ret; 934596ba34bSRafael J. Wysocki } 935596ba34bSRafael J. Wysocki 936596ba34bSRafael J. Wysocki /** 9370496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 938596ba34bSRafael J. Wysocki * @dev: Device to freeze. 939596ba34bSRafael J. Wysocki * 940596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 941596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 942596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 943596ba34bSRafael J. Wysocki * devices. 944596ba34bSRafael J. Wysocki */ 945596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 946596ba34bSRafael J. Wysocki { 947596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 948122a2237SUlf Hansson int ret = 0; 949596ba34bSRafael J. Wysocki 950596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 951596ba34bSRafael J. Wysocki 952596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 953596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 954596ba34bSRafael J. Wysocki return -EINVAL; 955596ba34bSRafael J. Wysocki 956122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 957122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 958122a2237SUlf Hansson 959122a2237SUlf Hansson return ret; 960596ba34bSRafael J. Wysocki } 961596ba34bSRafael J. Wysocki 962596ba34bSRafael J. Wysocki /** 9630496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 964596ba34bSRafael J. Wysocki * @dev: Device to thaw. 965596ba34bSRafael J. Wysocki * 9660496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 9670496c8aeSRafael J. Wysocki * before the system transition. 968596ba34bSRafael J. Wysocki */ 969596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 970596ba34bSRafael J. Wysocki { 971596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 972122a2237SUlf Hansson int ret = 0; 973596ba34bSRafael J. Wysocki 974596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 975596ba34bSRafael J. Wysocki 976596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 977596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 978596ba34bSRafael J. Wysocki return -EINVAL; 979596ba34bSRafael J. Wysocki 980122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 981122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 982122a2237SUlf Hansson 983122a2237SUlf Hansson return ret; 9840496c8aeSRafael J. Wysocki } 985596ba34bSRafael J. Wysocki 9860496c8aeSRafael J. Wysocki /** 9870496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 988596ba34bSRafael J. Wysocki * @dev: Device to resume. 989596ba34bSRafael J. Wysocki * 9900496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 9910496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 992596ba34bSRafael J. Wysocki */ 993596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 994596ba34bSRafael J. Wysocki { 995596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 996122a2237SUlf Hansson int ret = 0; 997596ba34bSRafael J. Wysocki 998596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 999596ba34bSRafael J. Wysocki 1000596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1001596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1002596ba34bSRafael J. Wysocki return -EINVAL; 1003596ba34bSRafael J. Wysocki 1004596ba34bSRafael J. Wysocki /* 1005596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1006596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1007596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 100865533bbfSRafael J. Wysocki * 100965533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 101065533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 101165533bbfSRafael J. Wysocki */ 101239dd0f23SUlf Hansson if (genpd->suspended_count++ == 0) 101365533bbfSRafael J. Wysocki /* 101465533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 101586e12eacSUlf Hansson * so make it appear as powered off to genpd_sync_power_on(), 1016802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1017596ba34bSRafael J. Wysocki */ 101817b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 101918dd2eceSRafael J. Wysocki 102086e12eacSUlf Hansson genpd_sync_power_on(genpd); 1021596ba34bSRafael J. Wysocki 1022122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 1023122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 1024122a2237SUlf Hansson 1025122a2237SUlf Hansson return ret; 1026596ba34bSRafael J. Wysocki } 1027596ba34bSRafael J. Wysocki 1028596ba34bSRafael J. Wysocki /** 1029596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1030596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1031596ba34bSRafael J. Wysocki * 1032596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1033596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1034596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1035596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1036596ba34bSRafael J. Wysocki */ 1037596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1038596ba34bSRafael J. Wysocki { 1039596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1040596ba34bSRafael J. Wysocki 1041596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1042596ba34bSRafael J. Wysocki 1043596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1044596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1045596ba34bSRafael J. Wysocki return; 1046596ba34bSRafael J. Wysocki 10474d23a5e8SUlf Hansson pm_generic_complete(dev); 10484d23a5e8SUlf Hansson 104935241d12SLina Iyer genpd_lock(genpd); 1050596ba34bSRafael J. Wysocki 105139dd0f23SUlf Hansson genpd->prepared_count--; 10524d23a5e8SUlf Hansson if (!genpd->prepared_count) 10534d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 1054596ba34bSRafael J. Wysocki 105535241d12SLina Iyer genpd_unlock(genpd); 1056596ba34bSRafael J. Wysocki } 1057596ba34bSRafael J. Wysocki 105877f827deSRafael J. Wysocki /** 1059d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 106077f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 106177f827deSRafael J. Wysocki * 106277f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 106377f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 106477f827deSRafael J. Wysocki */ 1065d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 106677f827deSRafael J. Wysocki { 106777f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 106877f827deSRafael J. Wysocki 106977f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 107077f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 107177f827deSRafael J. Wysocki return; 107277f827deSRafael J. Wysocki 107377f827deSRafael J. Wysocki if (suspend) { 107477f827deSRafael J. Wysocki genpd->suspended_count++; 107586e12eacSUlf Hansson genpd_sync_power_off(genpd); 107677f827deSRafael J. Wysocki } else { 107786e12eacSUlf Hansson genpd_sync_power_on(genpd); 107877f827deSRafael J. Wysocki genpd->suspended_count--; 107977f827deSRafael J. Wysocki } 108077f827deSRafael J. Wysocki } 1081d47e6464SUlf Hansson 1082d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1083d47e6464SUlf Hansson { 1084d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1085d47e6464SUlf Hansson } 1086d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1087d47e6464SUlf Hansson 1088d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1089d47e6464SUlf Hansson { 1090d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1091d47e6464SUlf Hansson } 1092d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 109377f827deSRafael J. Wysocki 1094d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1095596ba34bSRafael J. Wysocki 1096596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1097596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 1098596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1099596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 1100596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1101596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1102596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1103596ba34bSRafael J. Wysocki 1104596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1105596ba34bSRafael J. Wysocki 1106f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1107f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1108f104e1e5SUlf Hansson struct gpd_timing_data *td) 11091d5fcfecSRafael J. Wysocki { 11101d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11113e235685SUlf Hansson int ret; 11123e235685SUlf Hansson 11133e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11143e235685SUlf Hansson if (ret) 11153e235685SUlf Hansson return ERR_PTR(ret); 11161d5fcfecSRafael J. Wysocki 11171d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 11183e235685SUlf Hansson if (!gpd_data) { 11193e235685SUlf Hansson ret = -ENOMEM; 11203e235685SUlf Hansson goto err_put; 11213e235685SUlf Hansson } 11221d5fcfecSRafael J. Wysocki 1123f104e1e5SUlf Hansson if (td) 1124f104e1e5SUlf Hansson gpd_data->td = *td; 1125f104e1e5SUlf Hansson 1126f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1127f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1128f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1129f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1130f104e1e5SUlf Hansson 1131f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1132f104e1e5SUlf Hansson 1133f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1134f104e1e5SUlf Hansson ret = -EINVAL; 1135f104e1e5SUlf Hansson goto err_free; 1136f104e1e5SUlf Hansson } 1137f104e1e5SUlf Hansson 1138f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1139f104e1e5SUlf Hansson 1140f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1141f104e1e5SUlf Hansson 1142989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1143989561deSTomeu Vizoso 11441d5fcfecSRafael J. Wysocki return gpd_data; 11453e235685SUlf Hansson 1146f104e1e5SUlf Hansson err_free: 1147f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1148f104e1e5SUlf Hansson kfree(gpd_data); 11493e235685SUlf Hansson err_put: 11503e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11513e235685SUlf Hansson return ERR_PTR(ret); 11521d5fcfecSRafael J. Wysocki } 11531d5fcfecSRafael J. Wysocki 115449d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 11551d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 11561d5fcfecSRafael J. Wysocki { 1157989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1158989561deSTomeu Vizoso 1159f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1160f104e1e5SUlf Hansson 1161f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1162f104e1e5SUlf Hansson 1163f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1164f104e1e5SUlf Hansson 11651d5fcfecSRafael J. Wysocki kfree(gpd_data); 11663e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11671d5fcfecSRafael J. Wysocki } 11681d5fcfecSRafael J. Wysocki 116919efa5ffSJon Hunter static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1170b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1171f721889fSRafael J. Wysocki { 1172c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1173f721889fSRafael J. Wysocki int ret = 0; 1174f721889fSRafael J. Wysocki 1175f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1176f721889fSRafael J. Wysocki 1177f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1178f721889fSRafael J. Wysocki return -EINVAL; 1179f721889fSRafael J. Wysocki 1180f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 11813e235685SUlf Hansson if (IS_ERR(gpd_data)) 11823e235685SUlf Hansson return PTR_ERR(gpd_data); 11836ff7bb0dSRafael J. Wysocki 118435241d12SLina Iyer genpd_lock(genpd); 1185f721889fSRafael J. Wysocki 1186596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1187596ba34bSRafael J. Wysocki ret = -EAGAIN; 1188596ba34bSRafael J. Wysocki goto out; 1189596ba34bSRafael J. Wysocki } 1190596ba34bSRafael J. Wysocki 1191b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1192b472c2faSUlf Hansson if (ret) 1193b472c2faSUlf Hansson goto out; 1194d79b6fe1SGeert Uytterhoeven 119514b53064SUlf Hansson genpd->device_count++; 119614b53064SUlf Hansson genpd->max_off_time_changed = true; 119714b53064SUlf Hansson 11981d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 11996ff7bb0dSRafael J. Wysocki 1200f721889fSRafael J. Wysocki out: 120135241d12SLina Iyer genpd_unlock(genpd); 1202f721889fSRafael J. Wysocki 1203c0356db7SUlf Hansson if (ret) 1204c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1205c0356db7SUlf Hansson else 1206c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12071d5fcfecSRafael J. Wysocki 1208f721889fSRafael J. Wysocki return ret; 1209f721889fSRafael J. Wysocki } 121019efa5ffSJon Hunter 121119efa5ffSJon Hunter /** 121219efa5ffSJon Hunter * __pm_genpd_add_device - Add a device to an I/O PM domain. 121319efa5ffSJon Hunter * @genpd: PM domain to add the device to. 121419efa5ffSJon Hunter * @dev: Device to be added. 121519efa5ffSJon Hunter * @td: Set of PM QoS timing parameters to attach to the device. 121619efa5ffSJon Hunter */ 121719efa5ffSJon Hunter int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 121819efa5ffSJon Hunter struct gpd_timing_data *td) 121919efa5ffSJon Hunter { 122019efa5ffSJon Hunter int ret; 122119efa5ffSJon Hunter 122219efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 122319efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, td); 122419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 122519efa5ffSJon Hunter 122619efa5ffSJon Hunter return ret; 122719efa5ffSJon Hunter } 122824c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1229f721889fSRafael J. Wysocki 123085168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1231f721889fSRafael J. Wysocki struct device *dev) 1232f721889fSRafael J. Wysocki { 12336ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12344605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1235efa69025SRafael J. Wysocki int ret = 0; 1236f721889fSRafael J. Wysocki 1237f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1238f721889fSRafael J. Wysocki 1239c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1240c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1241c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1242c0356db7SUlf Hansson 124335241d12SLina Iyer genpd_lock(genpd); 1244f721889fSRafael J. Wysocki 1245596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1246596ba34bSRafael J. Wysocki ret = -EAGAIN; 1247596ba34bSRafael J. Wysocki goto out; 1248596ba34bSRafael J. Wysocki } 1249596ba34bSRafael J. Wysocki 12506ff7bb0dSRafael J. Wysocki genpd->device_count--; 12516ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 12526ff7bb0dSRafael J. Wysocki 1253d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1254c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1255d79b6fe1SGeert Uytterhoeven 1256efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 12576ff7bb0dSRafael J. Wysocki 125835241d12SLina Iyer genpd_unlock(genpd); 12596ff7bb0dSRafael J. Wysocki 126049d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 12611d5fcfecSRafael J. Wysocki 12626ff7bb0dSRafael J. Wysocki return 0; 1263f721889fSRafael J. Wysocki 1264596ba34bSRafael J. Wysocki out: 126535241d12SLina Iyer genpd_unlock(genpd); 1266c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1267f721889fSRafael J. Wysocki 1268f721889fSRafael J. Wysocki return ret; 1269f721889fSRafael J. Wysocki } 127085168d56SUlf Hansson 127185168d56SUlf Hansson /** 127285168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 127385168d56SUlf Hansson * @genpd: PM domain to remove the device from. 127485168d56SUlf Hansson * @dev: Device to be removed. 127585168d56SUlf Hansson */ 127685168d56SUlf Hansson int pm_genpd_remove_device(struct generic_pm_domain *genpd, 127785168d56SUlf Hansson struct device *dev) 127885168d56SUlf Hansson { 127985168d56SUlf Hansson if (!genpd || genpd != genpd_lookup_dev(dev)) 128085168d56SUlf Hansson return -EINVAL; 128185168d56SUlf Hansson 128285168d56SUlf Hansson return genpd_remove_device(genpd, dev); 128385168d56SUlf Hansson } 128424c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1285f721889fSRafael J. Wysocki 128619efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1287bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1288f721889fSRafael J. Wysocki { 12892547923dSLina Iyer struct gpd_link *link, *itr; 1290f721889fSRafael J. Wysocki int ret = 0; 1291f721889fSRafael J. Wysocki 1292fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1293fb7268beSRafael J. Wysocki || genpd == subdomain) 1294f721889fSRafael J. Wysocki return -EINVAL; 1295f721889fSRafael J. Wysocki 1296d716f479SLina Iyer /* 1297d716f479SLina Iyer * If the domain can be powered on/off in an IRQ safe 1298d716f479SLina Iyer * context, ensure that the subdomain can also be 1299d716f479SLina Iyer * powered on/off in that context. 1300d716f479SLina Iyer */ 1301d716f479SLina Iyer if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 130244cae7d5SDan Carpenter WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 1303d716f479SLina Iyer genpd->name, subdomain->name); 1304d716f479SLina Iyer return -EINVAL; 1305d716f479SLina Iyer } 1306d716f479SLina Iyer 13072547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13082547923dSLina Iyer if (!link) 13092547923dSLina Iyer return -ENOMEM; 13102547923dSLina Iyer 131135241d12SLina Iyer genpd_lock(subdomain); 131235241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1313f721889fSRafael J. Wysocki 131417b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1315bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1316f721889fSRafael J. Wysocki ret = -EINVAL; 1317f721889fSRafael J. Wysocki goto out; 1318f721889fSRafael J. Wysocki } 1319f721889fSRafael J. Wysocki 13202547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 13212547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1322f721889fSRafael J. Wysocki ret = -EINVAL; 1323f721889fSRafael J. Wysocki goto out; 1324f721889fSRafael J. Wysocki } 1325f721889fSRafael J. Wysocki } 1326f721889fSRafael J. Wysocki 13275063ce15SRafael J. Wysocki link->master = genpd; 13285063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1329bc0403ffSRafael J. Wysocki link->slave = subdomain; 1330bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1331bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1332c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1333f721889fSRafael J. Wysocki 1334f721889fSRafael J. Wysocki out: 133535241d12SLina Iyer genpd_unlock(genpd); 133635241d12SLina Iyer genpd_unlock(subdomain); 13372547923dSLina Iyer if (ret) 13382547923dSLina Iyer kfree(link); 1339f721889fSRafael J. Wysocki return ret; 1340f721889fSRafael J. Wysocki } 134119efa5ffSJon Hunter 134219efa5ffSJon Hunter /** 134319efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 134419efa5ffSJon Hunter * @genpd: Master PM domain to add the subdomain to. 134519efa5ffSJon Hunter * @subdomain: Subdomain to be added. 134619efa5ffSJon Hunter */ 134719efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 134819efa5ffSJon Hunter struct generic_pm_domain *subdomain) 134919efa5ffSJon Hunter { 135019efa5ffSJon Hunter int ret; 135119efa5ffSJon Hunter 135219efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 135319efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 135419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 135519efa5ffSJon Hunter 135619efa5ffSJon Hunter return ret; 135719efa5ffSJon Hunter } 1358d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1359f721889fSRafael J. Wysocki 1360f721889fSRafael J. Wysocki /** 1361f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1362f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 13635063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1364f721889fSRafael J. Wysocki */ 1365f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 13665063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1367f721889fSRafael J. Wysocki { 13685063ce15SRafael J. Wysocki struct gpd_link *link; 1369f721889fSRafael J. Wysocki int ret = -EINVAL; 1370f721889fSRafael J. Wysocki 13715063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1372f721889fSRafael J. Wysocki return -EINVAL; 1373f721889fSRafael J. Wysocki 137435241d12SLina Iyer genpd_lock(subdomain); 137535241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1376f721889fSRafael J. Wysocki 1377beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 137830e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 137930e7a65bSJon Hunter subdomain->name); 138030e7a65bSJon Hunter ret = -EBUSY; 138130e7a65bSJon Hunter goto out; 138230e7a65bSJon Hunter } 138330e7a65bSJon Hunter 13845063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 13855063ce15SRafael J. Wysocki if (link->slave != subdomain) 1386f721889fSRafael J. Wysocki continue; 1387f721889fSRafael J. Wysocki 13885063ce15SRafael J. Wysocki list_del(&link->master_node); 13895063ce15SRafael J. Wysocki list_del(&link->slave_node); 13905063ce15SRafael J. Wysocki kfree(link); 139117b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1392f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1393f721889fSRafael J. Wysocki 1394f721889fSRafael J. Wysocki ret = 0; 1395f721889fSRafael J. Wysocki break; 1396f721889fSRafael J. Wysocki } 1397f721889fSRafael J. Wysocki 139830e7a65bSJon Hunter out: 139935241d12SLina Iyer genpd_unlock(genpd); 140035241d12SLina Iyer genpd_unlock(subdomain); 1401f721889fSRafael J. Wysocki 1402f721889fSRafael J. Wysocki return ret; 1403f721889fSRafael J. Wysocki } 1404d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1405f721889fSRafael J. Wysocki 140659d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd) 140759d65b73SLina Iyer { 140859d65b73SLina Iyer struct genpd_power_state *state; 140959d65b73SLina Iyer 141059d65b73SLina Iyer state = kzalloc(sizeof(*state), GFP_KERNEL); 141159d65b73SLina Iyer if (!state) 141259d65b73SLina Iyer return -ENOMEM; 141359d65b73SLina Iyer 141459d65b73SLina Iyer genpd->states = state; 141559d65b73SLina Iyer genpd->state_count = 1; 141659d65b73SLina Iyer genpd->free = state; 141759d65b73SLina Iyer 141859d65b73SLina Iyer return 0; 141959d65b73SLina Iyer } 142059d65b73SLina Iyer 1421d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd) 1422d716f479SLina Iyer { 1423d716f479SLina Iyer if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 1424d716f479SLina Iyer spin_lock_init(&genpd->slock); 1425d716f479SLina Iyer genpd->lock_ops = &genpd_spin_ops; 1426d716f479SLina Iyer } else { 1427d716f479SLina Iyer mutex_init(&genpd->mlock); 1428d716f479SLina Iyer genpd->lock_ops = &genpd_mtx_ops; 1429d716f479SLina Iyer } 1430d716f479SLina Iyer } 1431d716f479SLina Iyer 1432d23b9b00SRafael J. Wysocki /** 1433f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1434f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1435f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1436f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 14377eb231c3SUlf Hansson * 14387eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1439f721889fSRafael J. Wysocki */ 14407eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1441f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1442f721889fSRafael J. Wysocki { 144359d65b73SLina Iyer int ret; 144459d65b73SLina Iyer 1445f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 14467eb231c3SUlf Hansson return -EINVAL; 1447f721889fSRafael J. Wysocki 14485063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 14495063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1450f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1451d716f479SLina Iyer genpd_lock_init(genpd); 1452f721889fSRafael J. Wysocki genpd->gov = gov; 1453f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1454c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 145517b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1456596ba34bSRafael J. Wysocki genpd->device_count = 0; 1457221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 14586ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1459de0aa06dSJon Hunter genpd->provider = NULL; 1460de0aa06dSJon Hunter genpd->has_provider = false; 1461795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1462795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 1463596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1464596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1465596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1466596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1467596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1468d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1469596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1470596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1471c11f6f5bSUlf Hansson 1472c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1473c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1474c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1475c11f6f5bSUlf Hansson } 1476c11f6f5bSUlf Hansson 1477fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 147859d65b73SLina Iyer if (genpd->state_count == 0) { 147959d65b73SLina Iyer ret = genpd_set_default_power_state(genpd); 148059d65b73SLina Iyer if (ret) 148159d65b73SLina Iyer return ret; 148259d65b73SLina Iyer } 1483fc5cbf0cSAxel Haslam 14845125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 14855125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 14865125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 14877eb231c3SUlf Hansson 14887eb231c3SUlf Hansson return 0; 14895125bbf3SRafael J. Wysocki } 1490be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1491aa42240aSTomasz Figa 14923fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 14933fe57710SJon Hunter { 14943fe57710SJon Hunter struct gpd_link *l, *link; 14953fe57710SJon Hunter 14963fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 14973fe57710SJon Hunter return -EINVAL; 14983fe57710SJon Hunter 149935241d12SLina Iyer genpd_lock(genpd); 15003fe57710SJon Hunter 15013fe57710SJon Hunter if (genpd->has_provider) { 150235241d12SLina Iyer genpd_unlock(genpd); 15033fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 15043fe57710SJon Hunter return -EBUSY; 15053fe57710SJon Hunter } 15063fe57710SJon Hunter 15073fe57710SJon Hunter if (!list_empty(&genpd->master_links) || genpd->device_count) { 150835241d12SLina Iyer genpd_unlock(genpd); 15093fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 15103fe57710SJon Hunter return -EBUSY; 15113fe57710SJon Hunter } 15123fe57710SJon Hunter 15133fe57710SJon Hunter list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { 15143fe57710SJon Hunter list_del(&link->master_node); 15153fe57710SJon Hunter list_del(&link->slave_node); 15163fe57710SJon Hunter kfree(link); 15173fe57710SJon Hunter } 15183fe57710SJon Hunter 15193fe57710SJon Hunter list_del(&genpd->gpd_list_node); 152035241d12SLina Iyer genpd_unlock(genpd); 15213fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 152259d65b73SLina Iyer kfree(genpd->free); 15233fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 15243fe57710SJon Hunter 15253fe57710SJon Hunter return 0; 15263fe57710SJon Hunter } 15273fe57710SJon Hunter 15283fe57710SJon Hunter /** 15293fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 15303fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 15313fe57710SJon Hunter * 15323fe57710SJon Hunter * To remove the PM domain, this function: 15333fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 15343fe57710SJon Hunter * if it was added. 15353fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 15363fe57710SJon Hunter * 15373fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 15383fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 15393fe57710SJon Hunter * devices associated with it. 15403fe57710SJon Hunter */ 15413fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 15423fe57710SJon Hunter { 15433fe57710SJon Hunter int ret; 15443fe57710SJon Hunter 15453fe57710SJon Hunter mutex_lock(&gpd_list_lock); 15463fe57710SJon Hunter ret = genpd_remove(genpd); 15473fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 15483fe57710SJon Hunter 15493fe57710SJon Hunter return ret; 15503fe57710SJon Hunter } 15513fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 15523fe57710SJon Hunter 1553aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1554892ebdccSJon Hunter 1555892ebdccSJon Hunter typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, 1556892ebdccSJon Hunter void *data); 1557892ebdccSJon Hunter 1558aa42240aSTomasz Figa /* 1559aa42240aSTomasz Figa * Device Tree based PM domain providers. 1560aa42240aSTomasz Figa * 1561aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1562aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1563aa42240aSTomasz Figa * 1564aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1565aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1566aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1567aa42240aSTomasz Figa * 1568aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1569892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1570892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 1571aa42240aSTomasz Figa * index. 1572aa42240aSTomasz Figa */ 1573aa42240aSTomasz Figa 1574aa42240aSTomasz Figa /** 1575aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1576aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1577aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1578aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1579aa42240aSTomasz Figa * into a PM domain. 1580aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1581aa42240aSTomasz Figa */ 1582aa42240aSTomasz Figa struct of_genpd_provider { 1583aa42240aSTomasz Figa struct list_head link; 1584aa42240aSTomasz Figa struct device_node *node; 1585aa42240aSTomasz Figa genpd_xlate_t xlate; 1586aa42240aSTomasz Figa void *data; 1587aa42240aSTomasz Figa }; 1588aa42240aSTomasz Figa 1589aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1590aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1591aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1592aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1593aa42240aSTomasz Figa 1594aa42240aSTomasz Figa /** 1595892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 1596aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1597aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1598aa42240aSTomasz Figa * 1599aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1600aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1601aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1602aa42240aSTomasz Figa */ 1603892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 1604aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1605aa42240aSTomasz Figa void *data) 1606aa42240aSTomasz Figa { 1607aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1608aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1609aa42240aSTomasz Figa return data; 1610aa42240aSTomasz Figa } 1611aa42240aSTomasz Figa 1612aa42240aSTomasz Figa /** 1613892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 1614aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1615aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1616aa42240aSTomasz Figa * 1617aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1618aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1619aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1620aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1621aa42240aSTomasz Figa */ 1622892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 1623aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1624aa42240aSTomasz Figa void *data) 1625aa42240aSTomasz Figa { 1626aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1627aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1628aa42240aSTomasz Figa 1629aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1630aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1631aa42240aSTomasz Figa 1632aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1633aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1634aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1635aa42240aSTomasz Figa } 1636aa42240aSTomasz Figa 1637aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1638aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1639aa42240aSTomasz Figa 1640aa42240aSTomasz Figa return genpd_data->domains[idx]; 1641aa42240aSTomasz Figa } 1642aa42240aSTomasz Figa 1643aa42240aSTomasz Figa /** 1644892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 1645aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1646aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1647aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1648aa42240aSTomasz Figa */ 1649892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1650aa42240aSTomasz Figa void *data) 1651aa42240aSTomasz Figa { 1652aa42240aSTomasz Figa struct of_genpd_provider *cp; 1653aa42240aSTomasz Figa 1654aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1655aa42240aSTomasz Figa if (!cp) 1656aa42240aSTomasz Figa return -ENOMEM; 1657aa42240aSTomasz Figa 1658aa42240aSTomasz Figa cp->node = of_node_get(np); 1659aa42240aSTomasz Figa cp->data = data; 1660aa42240aSTomasz Figa cp->xlate = xlate; 1661aa42240aSTomasz Figa 1662aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1663aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1664aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1665aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1666aa42240aSTomasz Figa 1667aa42240aSTomasz Figa return 0; 1668aa42240aSTomasz Figa } 1669892ebdccSJon Hunter 1670892ebdccSJon Hunter /** 1671892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 1672892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1673892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 1674892ebdccSJon Hunter */ 1675892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 1676892ebdccSJon Hunter struct generic_pm_domain *genpd) 1677892ebdccSJon Hunter { 16780159ec67SJon Hunter int ret = -EINVAL; 16790159ec67SJon Hunter 16800159ec67SJon Hunter if (!np || !genpd) 16810159ec67SJon Hunter return -EINVAL; 16820159ec67SJon Hunter 16830159ec67SJon Hunter mutex_lock(&gpd_list_lock); 16840159ec67SJon Hunter 16850159ec67SJon Hunter if (pm_genpd_present(genpd)) 16860159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 16870159ec67SJon Hunter 1688de0aa06dSJon Hunter if (!ret) { 1689de0aa06dSJon Hunter genpd->provider = &np->fwnode; 1690de0aa06dSJon Hunter genpd->has_provider = true; 1691de0aa06dSJon Hunter } 1692de0aa06dSJon Hunter 16930159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 16940159ec67SJon Hunter 16950159ec67SJon Hunter return ret; 1696892ebdccSJon Hunter } 1697892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 1698892ebdccSJon Hunter 1699892ebdccSJon Hunter /** 1700892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 1701892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1702892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 1703892ebdccSJon Hunter */ 1704892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 1705892ebdccSJon Hunter struct genpd_onecell_data *data) 1706892ebdccSJon Hunter { 17070159ec67SJon Hunter unsigned int i; 1708de0aa06dSJon Hunter int ret = -EINVAL; 17090159ec67SJon Hunter 17100159ec67SJon Hunter if (!np || !data) 17110159ec67SJon Hunter return -EINVAL; 17120159ec67SJon Hunter 17130159ec67SJon Hunter mutex_lock(&gpd_list_lock); 17140159ec67SJon Hunter 17150159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 1716609bed67STomeu Vizoso if (!data->domains[i]) 1717609bed67STomeu Vizoso continue; 1718de0aa06dSJon Hunter if (!pm_genpd_present(data->domains[i])) 1719de0aa06dSJon Hunter goto error; 1720de0aa06dSJon Hunter 1721de0aa06dSJon Hunter data->domains[i]->provider = &np->fwnode; 1722de0aa06dSJon Hunter data->domains[i]->has_provider = true; 17230159ec67SJon Hunter } 17240159ec67SJon Hunter 17250159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_onecell, data); 1726de0aa06dSJon Hunter if (ret < 0) 1727de0aa06dSJon Hunter goto error; 1728de0aa06dSJon Hunter 1729de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1730de0aa06dSJon Hunter 1731de0aa06dSJon Hunter return 0; 1732de0aa06dSJon Hunter 1733de0aa06dSJon Hunter error: 1734de0aa06dSJon Hunter while (i--) { 1735609bed67STomeu Vizoso if (!data->domains[i]) 1736609bed67STomeu Vizoso continue; 1737de0aa06dSJon Hunter data->domains[i]->provider = NULL; 1738de0aa06dSJon Hunter data->domains[i]->has_provider = false; 1739de0aa06dSJon Hunter } 17400159ec67SJon Hunter 17410159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 17420159ec67SJon Hunter 17430159ec67SJon Hunter return ret; 1744892ebdccSJon Hunter } 1745892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 1746aa42240aSTomasz Figa 1747aa42240aSTomasz Figa /** 1748aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1749aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1750aa42240aSTomasz Figa */ 1751aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1752aa42240aSTomasz Figa { 1753aa42240aSTomasz Figa struct of_genpd_provider *cp; 1754de0aa06dSJon Hunter struct generic_pm_domain *gpd; 1755aa42240aSTomasz Figa 1756de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 1757aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1758aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1759aa42240aSTomasz Figa if (cp->node == np) { 1760de0aa06dSJon Hunter /* 1761de0aa06dSJon Hunter * For each PM domain associated with the 1762de0aa06dSJon Hunter * provider, set the 'has_provider' to false 1763de0aa06dSJon Hunter * so that the PM domain can be safely removed. 1764de0aa06dSJon Hunter */ 1765de0aa06dSJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) 1766de0aa06dSJon Hunter if (gpd->provider == &np->fwnode) 1767de0aa06dSJon Hunter gpd->has_provider = false; 1768de0aa06dSJon Hunter 1769aa42240aSTomasz Figa list_del(&cp->link); 1770aa42240aSTomasz Figa of_node_put(cp->node); 1771aa42240aSTomasz Figa kfree(cp); 1772aa42240aSTomasz Figa break; 1773aa42240aSTomasz Figa } 1774aa42240aSTomasz Figa } 1775aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1776de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1777aa42240aSTomasz Figa } 1778aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1779aa42240aSTomasz Figa 1780aa42240aSTomasz Figa /** 1781f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 1782aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1783aa42240aSTomasz Figa * 1784aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1785aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1786aa42240aSTomasz Figa * domain. 1787aa42240aSTomasz Figa * 1788aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1789aa42240aSTomasz Figa * on failure. 1790aa42240aSTomasz Figa */ 1791f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 1792aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1793aa42240aSTomasz Figa { 1794aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1795aa42240aSTomasz Figa struct of_genpd_provider *provider; 1796aa42240aSTomasz Figa 179741795a8aSJon Hunter if (!genpdspec) 179841795a8aSJon Hunter return ERR_PTR(-EINVAL); 179941795a8aSJon Hunter 1800aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1801aa42240aSTomasz Figa 1802aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1803aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1804aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1805aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1806aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1807aa42240aSTomasz Figa break; 1808aa42240aSTomasz Figa } 1809aa42240aSTomasz Figa 1810aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1811aa42240aSTomasz Figa 1812aa42240aSTomasz Figa return genpd; 1813aa42240aSTomasz Figa } 1814aa42240aSTomasz Figa 1815aa42240aSTomasz Figa /** 1816ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 1817ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 1818ec69572bSJon Hunter * @dev: Device to be added. 1819ec69572bSJon Hunter * 1820ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 1821ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 1822ec69572bSJon Hunter */ 1823ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 1824ec69572bSJon Hunter { 1825ec69572bSJon Hunter struct generic_pm_domain *genpd; 182619efa5ffSJon Hunter int ret; 182719efa5ffSJon Hunter 182819efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1829ec69572bSJon Hunter 1830f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 183119efa5ffSJon Hunter if (IS_ERR(genpd)) { 183219efa5ffSJon Hunter ret = PTR_ERR(genpd); 183319efa5ffSJon Hunter goto out; 183419efa5ffSJon Hunter } 1835ec69572bSJon Hunter 183619efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, NULL); 183719efa5ffSJon Hunter 183819efa5ffSJon Hunter out: 183919efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 184019efa5ffSJon Hunter 184119efa5ffSJon Hunter return ret; 1842ec69572bSJon Hunter } 1843ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 1844ec69572bSJon Hunter 1845ec69572bSJon Hunter /** 1846ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1847ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 1848ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 1849ec69572bSJon Hunter * 1850ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 1851ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 1852ec69572bSJon Hunter * negative error code on failure. 1853ec69572bSJon Hunter */ 1854ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 1855ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 1856ec69572bSJon Hunter { 1857ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 185819efa5ffSJon Hunter int ret; 185919efa5ffSJon Hunter 186019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1861ec69572bSJon Hunter 1862f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 186319efa5ffSJon Hunter if (IS_ERR(parent)) { 186419efa5ffSJon Hunter ret = PTR_ERR(parent); 186519efa5ffSJon Hunter goto out; 186619efa5ffSJon Hunter } 1867ec69572bSJon Hunter 1868f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 186919efa5ffSJon Hunter if (IS_ERR(subdomain)) { 187019efa5ffSJon Hunter ret = PTR_ERR(subdomain); 187119efa5ffSJon Hunter goto out; 187219efa5ffSJon Hunter } 1873ec69572bSJon Hunter 187419efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 187519efa5ffSJon Hunter 187619efa5ffSJon Hunter out: 187719efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 187819efa5ffSJon Hunter 187919efa5ffSJon Hunter return ret; 1880ec69572bSJon Hunter } 1881ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 1882ec69572bSJon Hunter 1883ec69572bSJon Hunter /** 188417926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 188517926551SJon Hunter * @provider: Pointer to device structure associated with provider 188617926551SJon Hunter * 188717926551SJon Hunter * Find the last PM domain that was added by a particular provider and 188817926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 188917926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 189017926551SJon Hunter * domain will only be removed, if the provider associated with domain 189117926551SJon Hunter * has been removed. 189217926551SJon Hunter * 189317926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 189417926551SJon Hunter * ERR_PTR() on failure. 189517926551SJon Hunter */ 189617926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 189717926551SJon Hunter { 189817926551SJon Hunter struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT); 189917926551SJon Hunter int ret; 190017926551SJon Hunter 190117926551SJon Hunter if (IS_ERR_OR_NULL(np)) 190217926551SJon Hunter return ERR_PTR(-EINVAL); 190317926551SJon Hunter 190417926551SJon Hunter mutex_lock(&gpd_list_lock); 190517926551SJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 190617926551SJon Hunter if (gpd->provider == &np->fwnode) { 190717926551SJon Hunter ret = genpd_remove(gpd); 190817926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 190917926551SJon Hunter break; 191017926551SJon Hunter } 191117926551SJon Hunter } 191217926551SJon Hunter mutex_unlock(&gpd_list_lock); 191317926551SJon Hunter 191417926551SJon Hunter return genpd; 191517926551SJon Hunter } 191617926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 191717926551SJon Hunter 191817926551SJon Hunter /** 1919aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 19208bb6944eSJon Hunter * @dev: Device to detach. 1921aa42240aSTomasz Figa * @power_off: Currently not used 1922aa42240aSTomasz Figa * 1923aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1924aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1925aa42240aSTomasz Figa */ 1926aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1927aa42240aSTomasz Figa { 1928446d999cSRussell King struct generic_pm_domain *pd; 192993af5e93SGeert Uytterhoeven unsigned int i; 1930aa42240aSTomasz Figa int ret = 0; 1931aa42240aSTomasz Figa 193285168d56SUlf Hansson pd = dev_to_genpd(dev); 193385168d56SUlf Hansson if (IS_ERR(pd)) 1934aa42240aSTomasz Figa return; 1935aa42240aSTomasz Figa 1936aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1937aa42240aSTomasz Figa 193893af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 193985168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 1940aa42240aSTomasz Figa if (ret != -EAGAIN) 1941aa42240aSTomasz Figa break; 194293af5e93SGeert Uytterhoeven 194393af5e93SGeert Uytterhoeven mdelay(i); 1944aa42240aSTomasz Figa cond_resched(); 1945aa42240aSTomasz Figa } 1946aa42240aSTomasz Figa 1947aa42240aSTomasz Figa if (ret < 0) { 1948aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1949aa42240aSTomasz Figa pd->name, ret); 1950aa42240aSTomasz Figa return; 1951aa42240aSTomasz Figa } 1952aa42240aSTomasz Figa 1953aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1954aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1955aa42240aSTomasz Figa } 1956aa42240aSTomasz Figa 1957632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1958632f7ce3SRussell King { 1959632f7ce3SRussell King struct generic_pm_domain *pd; 1960632f7ce3SRussell King 1961632f7ce3SRussell King pd = dev_to_genpd(dev); 1962632f7ce3SRussell King if (IS_ERR(pd)) 1963632f7ce3SRussell King return; 1964632f7ce3SRussell King 1965632f7ce3SRussell King genpd_queue_power_off_work(pd); 1966632f7ce3SRussell King } 1967632f7ce3SRussell King 1968aa42240aSTomasz Figa /** 1969aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1970aa42240aSTomasz Figa * @dev: Device to attach. 1971aa42240aSTomasz Figa * 1972aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1973aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1974aa42240aSTomasz Figa * 1975aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1976aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1977aa42240aSTomasz Figa * 1978311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1979311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1980311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1981311fa6adSJon Hunter * probed and to re-try again later. 1982aa42240aSTomasz Figa */ 1983aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 1984aa42240aSTomasz Figa { 1985aa42240aSTomasz Figa struct of_phandle_args pd_args; 1986aa42240aSTomasz Figa struct generic_pm_domain *pd; 198793af5e93SGeert Uytterhoeven unsigned int i; 1988aa42240aSTomasz Figa int ret; 1989aa42240aSTomasz Figa 1990aa42240aSTomasz Figa if (!dev->of_node) 1991aa42240aSTomasz Figa return -ENODEV; 1992aa42240aSTomasz Figa 1993aa42240aSTomasz Figa if (dev->pm_domain) 1994aa42240aSTomasz Figa return -EEXIST; 1995aa42240aSTomasz Figa 1996aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 1997aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 1998aa42240aSTomasz Figa if (ret < 0) { 1999aa42240aSTomasz Figa if (ret != -ENOENT) 2000aa42240aSTomasz Figa return ret; 2001aa42240aSTomasz Figa 2002aa42240aSTomasz Figa /* 2003aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 2004aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 2005aa42240aSTomasz Figa */ 2006aa42240aSTomasz Figa pd_args.args_count = 0; 2007aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 2008aa42240aSTomasz Figa "samsung,power-domain", 0); 2009aa42240aSTomasz Figa if (!pd_args.np) 2010aa42240aSTomasz Figa return -ENOENT; 2011aa42240aSTomasz Figa } 2012aa42240aSTomasz Figa 201319efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2014f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 2015265e2cf6SEric Anholt of_node_put(pd_args.np); 2016aa42240aSTomasz Figa if (IS_ERR(pd)) { 201719efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2018aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 2019aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 2020311fa6adSJon Hunter return -EPROBE_DEFER; 2021aa42240aSTomasz Figa } 2022aa42240aSTomasz Figa 2023aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 2024aa42240aSTomasz Figa 202593af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 202619efa5ffSJon Hunter ret = genpd_add_device(pd, dev, NULL); 2027aa42240aSTomasz Figa if (ret != -EAGAIN) 2028aa42240aSTomasz Figa break; 202993af5e93SGeert Uytterhoeven 203093af5e93SGeert Uytterhoeven mdelay(i); 2031aa42240aSTomasz Figa cond_resched(); 2032aa42240aSTomasz Figa } 203319efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2034aa42240aSTomasz Figa 2035aa42240aSTomasz Figa if (ret < 0) { 203634994692SGeert Uytterhoeven if (ret != -EPROBE_DEFER) 2037aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 2038aa42240aSTomasz Figa pd->name, ret); 2039311fa6adSJon Hunter goto out; 2040aa42240aSTomasz Figa } 2041aa42240aSTomasz Figa 2042aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 2043632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 2044aa42240aSTomasz Figa 204535241d12SLina Iyer genpd_lock(pd); 204686e12eacSUlf Hansson ret = genpd_power_on(pd, 0); 204735241d12SLina Iyer genpd_unlock(pd); 2048311fa6adSJon Hunter out: 2049311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 2050aa42240aSTomasz Figa } 2051aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 205230f60428SLina Iyer 205330f60428SLina Iyer static const struct of_device_id idle_state_match[] = { 2054598da548SLina Iyer { .compatible = "domain-idle-state", }, 205530f60428SLina Iyer { } 205630f60428SLina Iyer }; 205730f60428SLina Iyer 205830f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state, 205930f60428SLina Iyer struct device_node *state_node) 206030f60428SLina Iyer { 206130f60428SLina Iyer int err; 206230f60428SLina Iyer u32 residency; 206330f60428SLina Iyer u32 entry_latency, exit_latency; 206430f60428SLina Iyer const struct of_device_id *match_id; 206530f60428SLina Iyer 206630f60428SLina Iyer match_id = of_match_node(idle_state_match, state_node); 206730f60428SLina Iyer if (!match_id) 206830f60428SLina Iyer return -EINVAL; 206930f60428SLina Iyer 207030f60428SLina Iyer err = of_property_read_u32(state_node, "entry-latency-us", 207130f60428SLina Iyer &entry_latency); 207230f60428SLina Iyer if (err) { 207330f60428SLina Iyer pr_debug(" * %s missing entry-latency-us property\n", 207430f60428SLina Iyer state_node->full_name); 207530f60428SLina Iyer return -EINVAL; 207630f60428SLina Iyer } 207730f60428SLina Iyer 207830f60428SLina Iyer err = of_property_read_u32(state_node, "exit-latency-us", 207930f60428SLina Iyer &exit_latency); 208030f60428SLina Iyer if (err) { 208130f60428SLina Iyer pr_debug(" * %s missing exit-latency-us property\n", 208230f60428SLina Iyer state_node->full_name); 208330f60428SLina Iyer return -EINVAL; 208430f60428SLina Iyer } 208530f60428SLina Iyer 208630f60428SLina Iyer err = of_property_read_u32(state_node, "min-residency-us", &residency); 208730f60428SLina Iyer if (!err) 208830f60428SLina Iyer genpd_state->residency_ns = 1000 * residency; 208930f60428SLina Iyer 209030f60428SLina Iyer genpd_state->power_on_latency_ns = 1000 * exit_latency; 209130f60428SLina Iyer genpd_state->power_off_latency_ns = 1000 * entry_latency; 20920c9b694aSLina Iyer genpd_state->fwnode = &state_node->fwnode; 209330f60428SLina Iyer 209430f60428SLina Iyer return 0; 209530f60428SLina Iyer } 209630f60428SLina Iyer 209730f60428SLina Iyer /** 209830f60428SLina Iyer * of_genpd_parse_idle_states: Return array of idle states for the genpd. 209930f60428SLina Iyer * 210030f60428SLina Iyer * @dn: The genpd device node 210130f60428SLina Iyer * @states: The pointer to which the state array will be saved. 210230f60428SLina Iyer * @n: The count of elements in the array returned from this function. 210330f60428SLina Iyer * 210430f60428SLina Iyer * Returns the device states parsed from the OF node. The memory for the states 210530f60428SLina Iyer * is allocated by this function and is the responsibility of the caller to 210630f60428SLina Iyer * free the memory after use. 210730f60428SLina Iyer */ 210830f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn, 210930f60428SLina Iyer struct genpd_power_state **states, int *n) 211030f60428SLina Iyer { 211130f60428SLina Iyer struct genpd_power_state *st; 211230f60428SLina Iyer struct device_node *np; 211330f60428SLina Iyer int i = 0; 211430f60428SLina Iyer int err, ret; 211530f60428SLina Iyer int count; 211630f60428SLina Iyer struct of_phandle_iterator it; 211730f60428SLina Iyer 211830f60428SLina Iyer count = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 2119a1fee00dSColin Ian King if (count <= 0) 212030f60428SLina Iyer return -EINVAL; 212130f60428SLina Iyer 212230f60428SLina Iyer st = kcalloc(count, sizeof(*st), GFP_KERNEL); 212330f60428SLina Iyer if (!st) 212430f60428SLina Iyer return -ENOMEM; 212530f60428SLina Iyer 212630f60428SLina Iyer /* Loop over the phandles until all the requested entry is found */ 212730f60428SLina Iyer of_for_each_phandle(&it, err, dn, "domain-idle-states", NULL, 0) { 212830f60428SLina Iyer np = it.node; 212930f60428SLina Iyer ret = genpd_parse_state(&st[i++], np); 213030f60428SLina Iyer if (ret) { 213130f60428SLina Iyer pr_err 213230f60428SLina Iyer ("Parsing idle state node %s failed with err %d\n", 213330f60428SLina Iyer np->full_name, ret); 213430f60428SLina Iyer of_node_put(np); 213530f60428SLina Iyer kfree(st); 213630f60428SLina Iyer return ret; 213730f60428SLina Iyer } 213830f60428SLina Iyer } 213930f60428SLina Iyer 214030f60428SLina Iyer *n = count; 214130f60428SLina Iyer *states = st; 214230f60428SLina Iyer 214330f60428SLina Iyer return 0; 214430f60428SLina Iyer } 214530f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 214630f60428SLina Iyer 2147d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 21482bd5306aSMaciej Matraszek 21492bd5306aSMaciej Matraszek 21502bd5306aSMaciej Matraszek /*** debugfs support ***/ 21512bd5306aSMaciej Matraszek 21528b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 21532bd5306aSMaciej Matraszek #include <linux/pm.h> 21542bd5306aSMaciej Matraszek #include <linux/device.h> 21552bd5306aSMaciej Matraszek #include <linux/debugfs.h> 21562bd5306aSMaciej Matraszek #include <linux/seq_file.h> 21572bd5306aSMaciej Matraszek #include <linux/init.h> 21582bd5306aSMaciej Matraszek #include <linux/kobject.h> 21592bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 21602bd5306aSMaciej Matraszek 21612bd5306aSMaciej Matraszek /* 21622bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 2163d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 21642bd5306aSMaciej Matraszek */ 21652bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 21662bd5306aSMaciej Matraszek { 21672bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 21682bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 21692bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 21702bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 21712bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 21722bd5306aSMaciej Matraszek }; 21732bd5306aSMaciej Matraszek const char *p = ""; 21742bd5306aSMaciej Matraszek 21752bd5306aSMaciej Matraszek if (dev->power.runtime_error) 21762bd5306aSMaciej Matraszek p = "error"; 21772bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 21782bd5306aSMaciej Matraszek p = "unsupported"; 21792bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 21802bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 21812bd5306aSMaciej Matraszek else 21822bd5306aSMaciej Matraszek WARN_ON(1); 21832bd5306aSMaciej Matraszek 21842bd5306aSMaciej Matraszek seq_puts(s, p); 21852bd5306aSMaciej Matraszek } 21862bd5306aSMaciej Matraszek 21872bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 218866a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 21892bd5306aSMaciej Matraszek { 21902bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 21912bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 21922bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 21932bd5306aSMaciej Matraszek }; 21942bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 21952bd5306aSMaciej Matraszek const char *kobj_path; 21962bd5306aSMaciej Matraszek struct gpd_link *link; 21976954d432SGeert Uytterhoeven char state[16]; 21982bd5306aSMaciej Matraszek int ret; 21992bd5306aSMaciej Matraszek 220035241d12SLina Iyer ret = genpd_lock_interruptible(genpd); 22012bd5306aSMaciej Matraszek if (ret) 22022bd5306aSMaciej Matraszek return -ERESTARTSYS; 22032bd5306aSMaciej Matraszek 220466a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 22052bd5306aSMaciej Matraszek goto exit; 2206fc5cbf0cSAxel Haslam if (genpd->status == GPD_STATE_POWER_OFF) 22070ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 22086954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 2209fc5cbf0cSAxel Haslam else 22106954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 22116954d432SGeert Uytterhoeven status_lookup[genpd->status]); 22126954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 22132bd5306aSMaciej Matraszek 22142bd5306aSMaciej Matraszek /* 22152bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 22162bd5306aSMaciej Matraszek * master and slave, so we are safe. 221766a5ca4bSKevin Hilman * Also genpd->name is immutable. 22182bd5306aSMaciej Matraszek */ 221966a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 22202bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 222166a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 22222bd5306aSMaciej Matraszek seq_puts(s, ", "); 22232bd5306aSMaciej Matraszek } 22242bd5306aSMaciej Matraszek 222566a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 2226d716f479SLina Iyer kobj_path = kobject_get_path(&pm_data->dev->kobj, 2227d716f479SLina Iyer genpd_is_irq_safe(genpd) ? 2228d716f479SLina Iyer GFP_ATOMIC : GFP_KERNEL); 22292bd5306aSMaciej Matraszek if (kobj_path == NULL) 22302bd5306aSMaciej Matraszek continue; 22312bd5306aSMaciej Matraszek 22322bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 22332bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 22342bd5306aSMaciej Matraszek kfree(kobj_path); 22352bd5306aSMaciej Matraszek } 22362bd5306aSMaciej Matraszek 22372bd5306aSMaciej Matraszek seq_puts(s, "\n"); 22382bd5306aSMaciej Matraszek exit: 223935241d12SLina Iyer genpd_unlock(genpd); 22402bd5306aSMaciej Matraszek 22412bd5306aSMaciej Matraszek return 0; 22422bd5306aSMaciej Matraszek } 22432bd5306aSMaciej Matraszek 22442bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 22452bd5306aSMaciej Matraszek { 224666a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 22472bd5306aSMaciej Matraszek int ret = 0; 22482bd5306aSMaciej Matraszek 22492bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 22502bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 22512bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 22522bd5306aSMaciej Matraszek 22532bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 22542bd5306aSMaciej Matraszek if (ret) 22552bd5306aSMaciej Matraszek return -ERESTARTSYS; 22562bd5306aSMaciej Matraszek 225766a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 225866a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 22592bd5306aSMaciej Matraszek if (ret) 22602bd5306aSMaciej Matraszek break; 22612bd5306aSMaciej Matraszek } 22622bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 22632bd5306aSMaciej Matraszek 22642bd5306aSMaciej Matraszek return ret; 22652bd5306aSMaciej Matraszek } 22662bd5306aSMaciej Matraszek 22672bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 22682bd5306aSMaciej Matraszek { 22692bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 22702bd5306aSMaciej Matraszek } 22712bd5306aSMaciej Matraszek 22722bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 22732bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 22742bd5306aSMaciej Matraszek .read = seq_read, 22752bd5306aSMaciej Matraszek .llseek = seq_lseek, 22762bd5306aSMaciej Matraszek .release = single_release, 22772bd5306aSMaciej Matraszek }; 22782bd5306aSMaciej Matraszek 22792bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 22802bd5306aSMaciej Matraszek { 22812bd5306aSMaciej Matraszek struct dentry *d; 22822bd5306aSMaciej Matraszek 22832bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 22842bd5306aSMaciej Matraszek 22852bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 22862bd5306aSMaciej Matraszek return -ENOMEM; 22872bd5306aSMaciej Matraszek 22882bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 22892bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 22902bd5306aSMaciej Matraszek if (!d) 22912bd5306aSMaciej Matraszek return -ENOMEM; 22922bd5306aSMaciej Matraszek 22932bd5306aSMaciej Matraszek return 0; 22942bd5306aSMaciej Matraszek } 22952bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 22962bd5306aSMaciej Matraszek 22972bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 22982bd5306aSMaciej Matraszek { 22992bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 23002bd5306aSMaciej Matraszek } 23012bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 23028b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 2303