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 204a4630c61SGeert Uytterhoeven 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 234a4630c61SGeert Uytterhoeven 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 /** 2657420aa4fSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff(). 266a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 26729e47e21SUlf Hansson * 2687420aa4fSUlf Hansson * Queue up the execution of genpd_poweroff() 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 /** 27753af16f7SUlf Hansson * genpd_poweron - 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 */ 28453af16f7SUlf Hansson static int genpd_poweron(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); 30353af16f7SUlf Hansson ret = genpd_poweron(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 312a4630c61SGeert Uytterhoeven 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 /** 3717420aa4fSUlf Hansson * genpd_poweroff - 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 */ 3787420aa4fSUlf Hansson static int genpd_poweroff(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 4307420aa4fSUlf Hansson * managed to call genpd_poweron() for the master yet after 4317420aa4fSUlf Hansson * incrementing it. In that case genpd_poweron() will wait 4323c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 4337420aa4fSUlf Hansson * the genpd_poweron() restore power for us (this shouldn't 4343c07cbc4SRafael J. Wysocki * happen very often). 4353c07cbc4SRafael J. Wysocki */ 436a4630c61SGeert Uytterhoeven 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); 4627420aa4fSUlf Hansson genpd_poweroff(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. */ 547ffe12855SUlf Hansson if (runtime_pm) 5482b1d88cdSUlf Hansson time_start = ktime_get(); 5492b1d88cdSUlf Hansson 55054eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 551f721889fSRafael J. Wysocki if (ret) 55217b75ecaSRafael J. Wysocki return ret; 55317b75ecaSRafael J. Wysocki 5542b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 555ba2bbfbfSUlf Hansson if (ret) { 55654eeddbfSUlf Hansson __genpd_runtime_resume(dev); 557ba2bbfbfSUlf Hansson return ret; 558ba2bbfbfSUlf Hansson } 559ba2bbfbfSUlf Hansson 5602b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 561ffe12855SUlf Hansson if (runtime_pm) { 5622b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5632b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 5642b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 5652b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 5662b1d88cdSUlf Hansson elapsed_ns); 5672b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5682b1d88cdSUlf Hansson td->constraint_changed = true; 5692b1d88cdSUlf Hansson } 570ffe12855SUlf Hansson } 5712b1d88cdSUlf Hansson 5720aa2a221SRafael J. Wysocki /* 573d716f479SLina Iyer * If power.irq_safe is set, this routine may be run with 574d716f479SLina Iyer * IRQs disabled, so suspend only if the PM domain also is irq_safe. 5750aa2a221SRafael J. Wysocki */ 576d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) 5770aa2a221SRafael J. Wysocki return 0; 5780aa2a221SRafael J. Wysocki 57935241d12SLina Iyer genpd_lock(genpd); 5807420aa4fSUlf Hansson genpd_poweroff(genpd, false); 58135241d12SLina Iyer genpd_unlock(genpd); 582f721889fSRafael J. Wysocki 583f721889fSRafael J. Wysocki return 0; 584f721889fSRafael J. Wysocki } 585f721889fSRafael J. Wysocki 586f721889fSRafael J. Wysocki /** 587795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 588f721889fSRafael J. Wysocki * @dev: Device to resume. 589f721889fSRafael J. Wysocki * 590f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 591f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 592f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 593f721889fSRafael J. Wysocki */ 594795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 595f721889fSRafael J. Wysocki { 596f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 5972b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 598ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 5992b1d88cdSUlf Hansson ktime_t time_start; 6002b1d88cdSUlf Hansson s64 elapsed_ns; 601f721889fSRafael J. Wysocki int ret; 602ba2bbfbfSUlf Hansson bool timed = true; 603f721889fSRafael J. Wysocki 604f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 605f721889fSRafael J. Wysocki 6065248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6075248051bSRafael J. Wysocki if (IS_ERR(genpd)) 608f721889fSRafael J. Wysocki return -EINVAL; 609f721889fSRafael J. Wysocki 610d716f479SLina Iyer /* 611d716f479SLina Iyer * As we don't power off a non IRQ safe domain, which holds 612d716f479SLina Iyer * an IRQ safe device, we don't need to restore power to it. 613d716f479SLina Iyer */ 614d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) { 615ba2bbfbfSUlf Hansson timed = false; 616ba2bbfbfSUlf Hansson goto out; 617ba2bbfbfSUlf Hansson } 6180aa2a221SRafael J. Wysocki 61935241d12SLina Iyer genpd_lock(genpd); 62053af16f7SUlf Hansson ret = genpd_poweron(genpd, 0); 62135241d12SLina Iyer genpd_unlock(genpd); 622ba2bbfbfSUlf Hansson 623ba2bbfbfSUlf Hansson if (ret) 6243f241775SRafael J. Wysocki return ret; 625c6d22b37SRafael J. Wysocki 626ba2bbfbfSUlf Hansson out: 6272b1d88cdSUlf Hansson /* Measure resume latency. */ 628ffe12855SUlf Hansson if (timed && runtime_pm) 6292b1d88cdSUlf Hansson time_start = ktime_get(); 6302b1d88cdSUlf Hansson 631076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 632076395caSLaurent Pinchart if (ret) 633076395caSLaurent Pinchart goto err_poweroff; 634076395caSLaurent Pinchart 63554eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 636076395caSLaurent Pinchart if (ret) 637076395caSLaurent Pinchart goto err_stop; 6382b1d88cdSUlf Hansson 6392b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 640ffe12855SUlf Hansson if (timed && runtime_pm) { 6412b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 6422b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 6432b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 6442b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 6452b1d88cdSUlf Hansson elapsed_ns); 6462b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 6472b1d88cdSUlf Hansson td->constraint_changed = true; 6482b1d88cdSUlf Hansson } 6492b1d88cdSUlf Hansson } 65017b75ecaSRafael J. Wysocki 651f721889fSRafael J. Wysocki return 0; 652076395caSLaurent Pinchart 653076395caSLaurent Pinchart err_stop: 654076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 655076395caSLaurent Pinchart err_poweroff: 656d716f479SLina Iyer if (!pm_runtime_is_irq_safe(dev) || 657d716f479SLina Iyer (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { 65835241d12SLina Iyer genpd_lock(genpd); 659076395caSLaurent Pinchart genpd_poweroff(genpd, 0); 66035241d12SLina Iyer genpd_unlock(genpd); 661076395caSLaurent Pinchart } 662076395caSLaurent Pinchart 663076395caSLaurent Pinchart return ret; 664f721889fSRafael J. Wysocki } 665f721889fSRafael J. Wysocki 66639ac5ba5STushar Behera static bool pd_ignore_unused; 66739ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 66839ac5ba5STushar Behera { 66939ac5ba5STushar Behera pd_ignore_unused = true; 67039ac5ba5STushar Behera return 1; 67139ac5ba5STushar Behera } 67239ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 67339ac5ba5STushar Behera 67417f2ae7fSRafael J. Wysocki /** 675bb4b72fcSUlf Hansson * genpd_poweroff_unused - Power off all PM domains with no devices in use. 67617f2ae7fSRafael J. Wysocki */ 677bb4b72fcSUlf Hansson static int __init genpd_poweroff_unused(void) 67817f2ae7fSRafael J. Wysocki { 67917f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 68017f2ae7fSRafael J. Wysocki 68139ac5ba5STushar Behera if (pd_ignore_unused) { 68239ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 683bb4b72fcSUlf Hansson return 0; 68439ac5ba5STushar Behera } 68539ac5ba5STushar Behera 68617f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 68717f2ae7fSRafael J. Wysocki 68817f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 68917f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 69017f2ae7fSRafael J. Wysocki 69117f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 69217f2ae7fSRafael J. Wysocki 6932fe71dcdSUlf Hansson return 0; 6942fe71dcdSUlf Hansson } 6952fe71dcdSUlf Hansson late_initcall(genpd_poweroff_unused); 6962fe71dcdSUlf Hansson 6970159ec67SJon Hunter #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) 698596ba34bSRafael J. Wysocki 69977f827deSRafael J. Wysocki /** 70077f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 70177f827deSRafael J. Wysocki * @genpd: PM domain to check. 70277f827deSRafael J. Wysocki */ 703895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 70477f827deSRafael J. Wysocki { 705895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 70677f827deSRafael J. Wysocki 70777f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 70877f827deSRafael J. Wysocki return false; 70977f827deSRafael J. Wysocki 71077f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 71177f827deSRafael J. Wysocki if (gpd == genpd) 71277f827deSRafael J. Wysocki return true; 71377f827deSRafael J. Wysocki 71477f827deSRafael J. Wysocki return false; 71577f827deSRafael J. Wysocki } 71677f827deSRafael J. Wysocki 7170159ec67SJon Hunter #endif 7180159ec67SJon Hunter 7190159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 7200159ec67SJon Hunter 721d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 722d5e4cbfeSRafael J. Wysocki struct device *dev) 723d5e4cbfeSRafael J. Wysocki { 724d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 725d5e4cbfeSRafael J. Wysocki } 726d5e4cbfeSRafael J. Wysocki 727596ba34bSRafael J. Wysocki /** 728eefdee07SUlf Hansson * genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 729596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 730596ba34bSRafael J. Wysocki * 731596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7325063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 733596ba34bSRafael J. Wysocki * 73477f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 73577f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 73677f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 73777f827deSRafael J. Wysocki * parallel). 738596ba34bSRafael J. Wysocki */ 739eefdee07SUlf Hansson static void genpd_sync_poweroff(struct generic_pm_domain *genpd) 740596ba34bSRafael J. Wysocki { 7415063ce15SRafael J. Wysocki struct gpd_link *link; 742596ba34bSRafael J. Wysocki 74317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 744596ba34bSRafael J. Wysocki return; 745596ba34bSRafael J. Wysocki 746c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 747c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 748596ba34bSRafael J. Wysocki return; 749596ba34bSRafael J. Wysocki 750fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 751fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 752adb560b3SUlf Hansson genpd_power_off(genpd, false); 753596ba34bSRafael J. Wysocki 75417b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7555063ce15SRafael J. Wysocki 7565063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7575063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 758eefdee07SUlf Hansson genpd_sync_poweroff(link->master); 759596ba34bSRafael J. Wysocki } 760596ba34bSRafael J. Wysocki } 761596ba34bSRafael J. Wysocki 762596ba34bSRafael J. Wysocki /** 763eefdee07SUlf Hansson * genpd_sync_poweron - Synchronously power on a PM domain and its masters. 764802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 765802d8b49SRafael J. Wysocki * 76677f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 76777f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 76877f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 76977f827deSRafael J. Wysocki * parallel). 770802d8b49SRafael J. Wysocki */ 771eefdee07SUlf Hansson static void genpd_sync_poweron(struct generic_pm_domain *genpd) 772802d8b49SRafael J. Wysocki { 773802d8b49SRafael J. Wysocki struct gpd_link *link; 774802d8b49SRafael J. Wysocki 775ba2bbfbfSUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 776802d8b49SRafael J. Wysocki return; 777802d8b49SRafael J. Wysocki 778802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 779eefdee07SUlf Hansson genpd_sync_poweron(link->master); 780802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 781802d8b49SRafael J. Wysocki } 782802d8b49SRafael J. Wysocki 783adb560b3SUlf Hansson genpd_power_on(genpd, false); 784802d8b49SRafael J. Wysocki 785802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 786802d8b49SRafael J. Wysocki } 787802d8b49SRafael J. Wysocki 788802d8b49SRafael J. Wysocki /** 7894ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 7904ecd6e65SRafael J. Wysocki * @dev: Device to check. 7914ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 7924ecd6e65SRafael J. Wysocki * 7934ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 7944ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 7954ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 7964ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 7974ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 7984ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 7994ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8004ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8014ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8024ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8034ecd6e65SRafael J. Wysocki */ 8044ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 8054ecd6e65SRafael J. Wysocki { 8064ecd6e65SRafael J. Wysocki bool active_wakeup; 8074ecd6e65SRafael J. Wysocki 8084ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8094ecd6e65SRafael J. Wysocki return false; 8104ecd6e65SRafael J. Wysocki 811d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8124ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8134ecd6e65SRafael J. Wysocki } 8144ecd6e65SRafael J. Wysocki 8154ecd6e65SRafael J. Wysocki /** 816596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 817596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 818596ba34bSRafael J. Wysocki * 819596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 820596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 821596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 822596ba34bSRafael J. Wysocki * consisting of I/O devices. 823596ba34bSRafael J. Wysocki */ 824596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 825596ba34bSRafael J. Wysocki { 826596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 827b6c10c84SRafael J. Wysocki int ret; 828596ba34bSRafael J. Wysocki 829596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 830596ba34bSRafael J. Wysocki 831596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 832596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 833596ba34bSRafael J. Wysocki return -EINVAL; 834596ba34bSRafael J. Wysocki 83517b75ecaSRafael J. Wysocki /* 83617b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 83717b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 83817b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 83917b75ecaSRafael J. Wysocki */ 8404ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8414ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8424ecd6e65SRafael J. Wysocki 84335241d12SLina Iyer genpd_lock(genpd); 844596ba34bSRafael J. Wysocki 84539dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 84665533bbfSRafael J. Wysocki genpd->suspended_count = 0; 84717b75ecaSRafael J. Wysocki 84835241d12SLina Iyer genpd_unlock(genpd); 849596ba34bSRafael J. Wysocki 850b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 851b6c10c84SRafael J. Wysocki if (ret) { 85235241d12SLina Iyer genpd_lock(genpd); 853b6c10c84SRafael J. Wysocki 85439dd0f23SUlf Hansson genpd->prepared_count--; 855b6c10c84SRafael J. Wysocki 85635241d12SLina Iyer genpd_unlock(genpd); 857b6c10c84SRafael J. Wysocki } 85817b75ecaSRafael J. Wysocki 859b6c10c84SRafael J. Wysocki return ret; 860596ba34bSRafael J. Wysocki } 861596ba34bSRafael J. Wysocki 862596ba34bSRafael J. Wysocki /** 8630496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 8640496c8aeSRafael J. Wysocki * @dev: Device to suspend. 8650496c8aeSRafael J. Wysocki * 8660496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 8670496c8aeSRafael J. Wysocki * been stopped. 8680496c8aeSRafael J. Wysocki */ 8690496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 8700496c8aeSRafael J. Wysocki { 8710496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 872122a2237SUlf Hansson int ret; 873596ba34bSRafael J. Wysocki 8740496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8750496c8aeSRafael J. Wysocki 8760496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 8770496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 8780496c8aeSRafael J. Wysocki return -EINVAL; 8790496c8aeSRafael J. Wysocki 88039dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 881d4f2d87aSRafael J. Wysocki return 0; 882d4f2d87aSRafael J. Wysocki 883122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) { 884122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 885122a2237SUlf Hansson if (ret) 886122a2237SUlf Hansson return ret; 887122a2237SUlf Hansson } 888122a2237SUlf Hansson 889596ba34bSRafael J. Wysocki /* 890596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 891596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 892596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 893596ba34bSRafael J. Wysocki */ 894596ba34bSRafael J. Wysocki genpd->suspended_count++; 895eefdee07SUlf Hansson genpd_sync_poweroff(genpd); 896596ba34bSRafael J. Wysocki 897596ba34bSRafael J. Wysocki return 0; 898596ba34bSRafael J. Wysocki } 899596ba34bSRafael J. Wysocki 900596ba34bSRafael J. Wysocki /** 9010496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 902596ba34bSRafael J. Wysocki * @dev: Device to resume. 903596ba34bSRafael J. Wysocki * 9040496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 905596ba34bSRafael J. Wysocki */ 906596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 907596ba34bSRafael J. Wysocki { 908596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 909122a2237SUlf Hansson int ret = 0; 910596ba34bSRafael J. Wysocki 911596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 912596ba34bSRafael J. Wysocki 913596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 914596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 915596ba34bSRafael J. Wysocki return -EINVAL; 916596ba34bSRafael J. Wysocki 91739dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 918596ba34bSRafael J. Wysocki return 0; 919596ba34bSRafael J. Wysocki 920596ba34bSRafael J. Wysocki /* 921596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 922596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 923596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 924596ba34bSRafael J. Wysocki */ 925eefdee07SUlf Hansson genpd_sync_poweron(genpd); 926596ba34bSRafael J. Wysocki genpd->suspended_count--; 927596ba34bSRafael J. Wysocki 928122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 929122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 930122a2237SUlf Hansson 931122a2237SUlf Hansson return ret; 932596ba34bSRafael J. Wysocki } 933596ba34bSRafael J. Wysocki 934596ba34bSRafael J. Wysocki /** 9350496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 936596ba34bSRafael J. Wysocki * @dev: Device to freeze. 937596ba34bSRafael J. Wysocki * 938596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 939596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 940596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 941596ba34bSRafael J. Wysocki * devices. 942596ba34bSRafael J. Wysocki */ 943596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 944596ba34bSRafael J. Wysocki { 945596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 946122a2237SUlf Hansson int ret = 0; 947596ba34bSRafael J. Wysocki 948596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 949596ba34bSRafael J. Wysocki 950596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 951596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 952596ba34bSRafael J. Wysocki return -EINVAL; 953596ba34bSRafael J. Wysocki 954122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 955122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 956122a2237SUlf Hansson 957122a2237SUlf Hansson return ret; 958596ba34bSRafael J. Wysocki } 959596ba34bSRafael J. Wysocki 960596ba34bSRafael J. Wysocki /** 9610496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 962596ba34bSRafael J. Wysocki * @dev: Device to thaw. 963596ba34bSRafael J. Wysocki * 9640496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 9650496c8aeSRafael J. Wysocki * before the system transition. 966596ba34bSRafael J. Wysocki */ 967596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 968596ba34bSRafael J. Wysocki { 969596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 970122a2237SUlf Hansson int ret = 0; 971596ba34bSRafael J. Wysocki 972596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 973596ba34bSRafael J. Wysocki 974596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 975596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 976596ba34bSRafael J. Wysocki return -EINVAL; 977596ba34bSRafael J. Wysocki 978122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 979122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 980122a2237SUlf Hansson 981122a2237SUlf Hansson return ret; 9820496c8aeSRafael J. Wysocki } 983596ba34bSRafael J. Wysocki 9840496c8aeSRafael J. Wysocki /** 9850496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 986596ba34bSRafael J. Wysocki * @dev: Device to resume. 987596ba34bSRafael J. Wysocki * 9880496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 9890496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 990596ba34bSRafael J. Wysocki */ 991596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 992596ba34bSRafael J. Wysocki { 993596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 994122a2237SUlf Hansson int ret = 0; 995596ba34bSRafael J. Wysocki 996596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 997596ba34bSRafael J. Wysocki 998596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 999596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1000596ba34bSRafael J. Wysocki return -EINVAL; 1001596ba34bSRafael J. Wysocki 1002596ba34bSRafael J. Wysocki /* 1003596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1004596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1005596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 100665533bbfSRafael J. Wysocki * 100765533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 100865533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 100965533bbfSRafael J. Wysocki */ 101039dd0f23SUlf Hansson if (genpd->suspended_count++ == 0) 101165533bbfSRafael J. Wysocki /* 101265533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1013eefdee07SUlf Hansson * so make it appear as powered off to genpd_sync_poweron(), 1014802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1015596ba34bSRafael J. Wysocki */ 101617b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 101718dd2eceSRafael J. Wysocki 1018eefdee07SUlf Hansson genpd_sync_poweron(genpd); 1019596ba34bSRafael J. Wysocki 1020122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 1021122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 1022122a2237SUlf Hansson 1023122a2237SUlf Hansson return ret; 1024596ba34bSRafael J. Wysocki } 1025596ba34bSRafael J. Wysocki 1026596ba34bSRafael J. Wysocki /** 1027596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1028596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1029596ba34bSRafael J. Wysocki * 1030596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1031596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1032596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1033596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1034596ba34bSRafael J. Wysocki */ 1035596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1036596ba34bSRafael J. Wysocki { 1037596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1038596ba34bSRafael J. Wysocki 1039596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1040596ba34bSRafael J. Wysocki 1041596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1042596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1043596ba34bSRafael J. Wysocki return; 1044596ba34bSRafael J. Wysocki 10454d23a5e8SUlf Hansson pm_generic_complete(dev); 10464d23a5e8SUlf Hansson 104735241d12SLina Iyer genpd_lock(genpd); 1048596ba34bSRafael J. Wysocki 104939dd0f23SUlf Hansson genpd->prepared_count--; 10504d23a5e8SUlf Hansson if (!genpd->prepared_count) 10514d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 1052596ba34bSRafael J. Wysocki 105335241d12SLina Iyer genpd_unlock(genpd); 1054596ba34bSRafael J. Wysocki } 1055596ba34bSRafael J. Wysocki 105677f827deSRafael J. Wysocki /** 1057d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 105877f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 105977f827deSRafael J. Wysocki * 106077f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 106177f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 106277f827deSRafael J. Wysocki */ 1063d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 106477f827deSRafael J. Wysocki { 106577f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 106677f827deSRafael J. Wysocki 106777f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 106877f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 106977f827deSRafael J. Wysocki return; 107077f827deSRafael J. Wysocki 107177f827deSRafael J. Wysocki if (suspend) { 107277f827deSRafael J. Wysocki genpd->suspended_count++; 1073eefdee07SUlf Hansson genpd_sync_poweroff(genpd); 107477f827deSRafael J. Wysocki } else { 1075eefdee07SUlf Hansson genpd_sync_poweron(genpd); 107677f827deSRafael J. Wysocki genpd->suspended_count--; 107777f827deSRafael J. Wysocki } 107877f827deSRafael J. Wysocki } 1079d47e6464SUlf Hansson 1080d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1081d47e6464SUlf Hansson { 1082d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1083d47e6464SUlf Hansson } 1084d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1085d47e6464SUlf Hansson 1086d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1087d47e6464SUlf Hansson { 1088d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1089d47e6464SUlf Hansson } 1090d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 109177f827deSRafael J. Wysocki 1092d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1093596ba34bSRafael J. Wysocki 1094596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1095596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 1096596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1097596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 1098596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1099596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1100596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1101596ba34bSRafael J. Wysocki 1102596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1103596ba34bSRafael J. Wysocki 1104f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1105f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1106f104e1e5SUlf Hansson struct gpd_timing_data *td) 11071d5fcfecSRafael J. Wysocki { 11081d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11093e235685SUlf Hansson int ret; 11103e235685SUlf Hansson 11113e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11123e235685SUlf Hansson if (ret) 11133e235685SUlf Hansson return ERR_PTR(ret); 11141d5fcfecSRafael J. Wysocki 11151d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 11163e235685SUlf Hansson if (!gpd_data) { 11173e235685SUlf Hansson ret = -ENOMEM; 11183e235685SUlf Hansson goto err_put; 11193e235685SUlf Hansson } 11201d5fcfecSRafael J. Wysocki 1121f104e1e5SUlf Hansson if (td) 1122f104e1e5SUlf Hansson gpd_data->td = *td; 1123f104e1e5SUlf Hansson 1124f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1125f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1126f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1127f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1128f104e1e5SUlf Hansson 1129f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1130f104e1e5SUlf Hansson 1131f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1132f104e1e5SUlf Hansson ret = -EINVAL; 1133f104e1e5SUlf Hansson goto err_free; 1134f104e1e5SUlf Hansson } 1135f104e1e5SUlf Hansson 1136f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1137f104e1e5SUlf Hansson 1138f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1139f104e1e5SUlf Hansson 1140989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1141989561deSTomeu Vizoso 11421d5fcfecSRafael J. Wysocki return gpd_data; 11433e235685SUlf Hansson 1144f104e1e5SUlf Hansson err_free: 1145f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1146f104e1e5SUlf Hansson kfree(gpd_data); 11473e235685SUlf Hansson err_put: 11483e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11493e235685SUlf Hansson return ERR_PTR(ret); 11501d5fcfecSRafael J. Wysocki } 11511d5fcfecSRafael J. Wysocki 115249d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 11531d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 11541d5fcfecSRafael J. Wysocki { 1155989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1156989561deSTomeu Vizoso 1157f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1158f104e1e5SUlf Hansson 1159f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1160f104e1e5SUlf Hansson 1161f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1162f104e1e5SUlf Hansson 11631d5fcfecSRafael J. Wysocki kfree(gpd_data); 11643e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11651d5fcfecSRafael J. Wysocki } 11661d5fcfecSRafael J. Wysocki 116719efa5ffSJon Hunter static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1168b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1169f721889fSRafael J. Wysocki { 1170c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1171f721889fSRafael J. Wysocki int ret = 0; 1172f721889fSRafael J. Wysocki 1173f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1174f721889fSRafael J. Wysocki 1175f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1176f721889fSRafael J. Wysocki return -EINVAL; 1177f721889fSRafael J. Wysocki 1178f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 11793e235685SUlf Hansson if (IS_ERR(gpd_data)) 11803e235685SUlf Hansson return PTR_ERR(gpd_data); 11816ff7bb0dSRafael J. Wysocki 118235241d12SLina Iyer genpd_lock(genpd); 1183f721889fSRafael J. Wysocki 1184596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1185596ba34bSRafael J. Wysocki ret = -EAGAIN; 1186596ba34bSRafael J. Wysocki goto out; 1187596ba34bSRafael J. Wysocki } 1188596ba34bSRafael J. Wysocki 1189b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1190b472c2faSUlf Hansson if (ret) 1191b472c2faSUlf Hansson goto out; 1192d79b6fe1SGeert Uytterhoeven 119314b53064SUlf Hansson genpd->device_count++; 119414b53064SUlf Hansson genpd->max_off_time_changed = true; 119514b53064SUlf Hansson 11961d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 11976ff7bb0dSRafael J. Wysocki 1198f721889fSRafael J. Wysocki out: 119935241d12SLina Iyer genpd_unlock(genpd); 1200f721889fSRafael J. Wysocki 1201c0356db7SUlf Hansson if (ret) 1202c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1203c0356db7SUlf Hansson else 1204c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12051d5fcfecSRafael J. Wysocki 1206f721889fSRafael J. Wysocki return ret; 1207f721889fSRafael J. Wysocki } 120819efa5ffSJon Hunter 120919efa5ffSJon Hunter /** 121019efa5ffSJon Hunter * __pm_genpd_add_device - Add a device to an I/O PM domain. 121119efa5ffSJon Hunter * @genpd: PM domain to add the device to. 121219efa5ffSJon Hunter * @dev: Device to be added. 121319efa5ffSJon Hunter * @td: Set of PM QoS timing parameters to attach to the device. 121419efa5ffSJon Hunter */ 121519efa5ffSJon Hunter int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 121619efa5ffSJon Hunter struct gpd_timing_data *td) 121719efa5ffSJon Hunter { 121819efa5ffSJon Hunter int ret; 121919efa5ffSJon Hunter 122019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 122119efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, td); 122219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 122319efa5ffSJon Hunter 122419efa5ffSJon Hunter return ret; 122519efa5ffSJon Hunter } 122624c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1227f721889fSRafael J. Wysocki 122885168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1229f721889fSRafael J. Wysocki struct device *dev) 1230f721889fSRafael J. Wysocki { 12316ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12324605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1233efa69025SRafael J. Wysocki int ret = 0; 1234f721889fSRafael J. Wysocki 1235f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1236f721889fSRafael J. Wysocki 1237c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1238c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1239c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1240c0356db7SUlf Hansson 124135241d12SLina Iyer genpd_lock(genpd); 1242f721889fSRafael J. Wysocki 1243596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1244596ba34bSRafael J. Wysocki ret = -EAGAIN; 1245596ba34bSRafael J. Wysocki goto out; 1246596ba34bSRafael J. Wysocki } 1247596ba34bSRafael J. Wysocki 12486ff7bb0dSRafael J. Wysocki genpd->device_count--; 12496ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 12506ff7bb0dSRafael J. Wysocki 1251d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1252c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1253d79b6fe1SGeert Uytterhoeven 1254efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 12556ff7bb0dSRafael J. Wysocki 125635241d12SLina Iyer genpd_unlock(genpd); 12576ff7bb0dSRafael J. Wysocki 125849d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 12591d5fcfecSRafael J. Wysocki 12606ff7bb0dSRafael J. Wysocki return 0; 1261f721889fSRafael J. Wysocki 1262596ba34bSRafael J. Wysocki out: 126335241d12SLina Iyer genpd_unlock(genpd); 1264c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1265f721889fSRafael J. Wysocki 1266f721889fSRafael J. Wysocki return ret; 1267f721889fSRafael J. Wysocki } 126885168d56SUlf Hansson 126985168d56SUlf Hansson /** 127085168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 127185168d56SUlf Hansson * @genpd: PM domain to remove the device from. 127285168d56SUlf Hansson * @dev: Device to be removed. 127385168d56SUlf Hansson */ 127485168d56SUlf Hansson int pm_genpd_remove_device(struct generic_pm_domain *genpd, 127585168d56SUlf Hansson struct device *dev) 127685168d56SUlf Hansson { 127785168d56SUlf Hansson if (!genpd || genpd != genpd_lookup_dev(dev)) 127885168d56SUlf Hansson return -EINVAL; 127985168d56SUlf Hansson 128085168d56SUlf Hansson return genpd_remove_device(genpd, dev); 128185168d56SUlf Hansson } 128224c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1283f721889fSRafael J. Wysocki 128419efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1285bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1286f721889fSRafael J. Wysocki { 12872547923dSLina Iyer struct gpd_link *link, *itr; 1288f721889fSRafael J. Wysocki int ret = 0; 1289f721889fSRafael J. Wysocki 1290fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1291fb7268beSRafael J. Wysocki || genpd == subdomain) 1292f721889fSRafael J. Wysocki return -EINVAL; 1293f721889fSRafael J. Wysocki 1294d716f479SLina Iyer /* 1295d716f479SLina Iyer * If the domain can be powered on/off in an IRQ safe 1296d716f479SLina Iyer * context, ensure that the subdomain can also be 1297d716f479SLina Iyer * powered on/off in that context. 1298d716f479SLina Iyer */ 1299d716f479SLina Iyer if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 130044cae7d5SDan Carpenter WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 1301d716f479SLina Iyer genpd->name, subdomain->name); 1302d716f479SLina Iyer return -EINVAL; 1303d716f479SLina Iyer } 1304d716f479SLina Iyer 13052547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13062547923dSLina Iyer if (!link) 13072547923dSLina Iyer return -ENOMEM; 13082547923dSLina Iyer 130935241d12SLina Iyer genpd_lock(subdomain); 131035241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1311f721889fSRafael J. Wysocki 131217b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1313bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1314f721889fSRafael J. Wysocki ret = -EINVAL; 1315f721889fSRafael J. Wysocki goto out; 1316f721889fSRafael J. Wysocki } 1317f721889fSRafael J. Wysocki 13182547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 13192547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1320f721889fSRafael J. Wysocki ret = -EINVAL; 1321f721889fSRafael J. Wysocki goto out; 1322f721889fSRafael J. Wysocki } 1323f721889fSRafael J. Wysocki } 1324f721889fSRafael J. Wysocki 13255063ce15SRafael J. Wysocki link->master = genpd; 13265063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1327bc0403ffSRafael J. Wysocki link->slave = subdomain; 1328bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1329bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1330c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1331f721889fSRafael J. Wysocki 1332f721889fSRafael J. Wysocki out: 133335241d12SLina Iyer genpd_unlock(genpd); 133435241d12SLina Iyer genpd_unlock(subdomain); 13352547923dSLina Iyer if (ret) 13362547923dSLina Iyer kfree(link); 1337f721889fSRafael J. Wysocki return ret; 1338f721889fSRafael J. Wysocki } 133919efa5ffSJon Hunter 134019efa5ffSJon Hunter /** 134119efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 134219efa5ffSJon Hunter * @genpd: Master PM domain to add the subdomain to. 134319efa5ffSJon Hunter * @subdomain: Subdomain to be added. 134419efa5ffSJon Hunter */ 134519efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 134619efa5ffSJon Hunter struct generic_pm_domain *subdomain) 134719efa5ffSJon Hunter { 134819efa5ffSJon Hunter int ret; 134919efa5ffSJon Hunter 135019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 135119efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 135219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 135319efa5ffSJon Hunter 135419efa5ffSJon Hunter return ret; 135519efa5ffSJon Hunter } 1356d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1357f721889fSRafael J. Wysocki 1358f721889fSRafael J. Wysocki /** 1359f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1360f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 13615063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1362f721889fSRafael J. Wysocki */ 1363f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 13645063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1365f721889fSRafael J. Wysocki { 13665063ce15SRafael J. Wysocki struct gpd_link *link; 1367f721889fSRafael J. Wysocki int ret = -EINVAL; 1368f721889fSRafael J. Wysocki 13695063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1370f721889fSRafael J. Wysocki return -EINVAL; 1371f721889fSRafael J. Wysocki 137235241d12SLina Iyer genpd_lock(subdomain); 137335241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1374f721889fSRafael J. Wysocki 1375beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 137630e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 137730e7a65bSJon Hunter subdomain->name); 137830e7a65bSJon Hunter ret = -EBUSY; 137930e7a65bSJon Hunter goto out; 138030e7a65bSJon Hunter } 138130e7a65bSJon Hunter 13825063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 13835063ce15SRafael J. Wysocki if (link->slave != subdomain) 1384f721889fSRafael J. Wysocki continue; 1385f721889fSRafael J. Wysocki 13865063ce15SRafael J. Wysocki list_del(&link->master_node); 13875063ce15SRafael J. Wysocki list_del(&link->slave_node); 13885063ce15SRafael J. Wysocki kfree(link); 138917b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1390f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1391f721889fSRafael J. Wysocki 1392f721889fSRafael J. Wysocki ret = 0; 1393f721889fSRafael J. Wysocki break; 1394f721889fSRafael J. Wysocki } 1395f721889fSRafael J. Wysocki 139630e7a65bSJon Hunter out: 139735241d12SLina Iyer genpd_unlock(genpd); 139835241d12SLina Iyer genpd_unlock(subdomain); 1399f721889fSRafael J. Wysocki 1400f721889fSRafael J. Wysocki return ret; 1401f721889fSRafael J. Wysocki } 1402d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1403f721889fSRafael J. Wysocki 140459d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd) 140559d65b73SLina Iyer { 140659d65b73SLina Iyer struct genpd_power_state *state; 140759d65b73SLina Iyer 140859d65b73SLina Iyer state = kzalloc(sizeof(*state), GFP_KERNEL); 140959d65b73SLina Iyer if (!state) 141059d65b73SLina Iyer return -ENOMEM; 141159d65b73SLina Iyer 141259d65b73SLina Iyer genpd->states = state; 141359d65b73SLina Iyer genpd->state_count = 1; 141459d65b73SLina Iyer genpd->free = state; 141559d65b73SLina Iyer 141659d65b73SLina Iyer return 0; 141759d65b73SLina Iyer } 141859d65b73SLina Iyer 1419d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd) 1420d716f479SLina Iyer { 1421d716f479SLina Iyer if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 1422d716f479SLina Iyer spin_lock_init(&genpd->slock); 1423d716f479SLina Iyer genpd->lock_ops = &genpd_spin_ops; 1424d716f479SLina Iyer } else { 1425d716f479SLina Iyer mutex_init(&genpd->mlock); 1426d716f479SLina Iyer genpd->lock_ops = &genpd_mtx_ops; 1427d716f479SLina Iyer } 1428d716f479SLina Iyer } 1429d716f479SLina Iyer 1430d23b9b00SRafael J. Wysocki /** 1431f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1432f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1433f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1434f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 14357eb231c3SUlf Hansson * 14367eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1437f721889fSRafael J. Wysocki */ 14387eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1439f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1440f721889fSRafael J. Wysocki { 144159d65b73SLina Iyer int ret; 144259d65b73SLina Iyer 1443f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 14447eb231c3SUlf Hansson return -EINVAL; 1445f721889fSRafael J. Wysocki 14465063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 14475063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1448f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1449d716f479SLina Iyer genpd_lock_init(genpd); 1450f721889fSRafael J. Wysocki genpd->gov = gov; 1451f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1452c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 145317b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1454596ba34bSRafael J. Wysocki genpd->device_count = 0; 1455221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 14566ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1457de0aa06dSJon Hunter genpd->provider = NULL; 1458de0aa06dSJon Hunter genpd->has_provider = false; 1459795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1460795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 1461596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1462596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1463596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1464596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1465596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1466d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1467596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1468596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1469c11f6f5bSUlf Hansson 1470c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1471c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1472c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1473c11f6f5bSUlf Hansson } 1474c11f6f5bSUlf Hansson 1475fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 147659d65b73SLina Iyer if (genpd->state_count == 0) { 147759d65b73SLina Iyer ret = genpd_set_default_power_state(genpd); 147859d65b73SLina Iyer if (ret) 147959d65b73SLina Iyer return ret; 148059d65b73SLina Iyer } 1481fc5cbf0cSAxel Haslam 14825125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 14835125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 14845125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 14857eb231c3SUlf Hansson 14867eb231c3SUlf Hansson return 0; 14875125bbf3SRafael J. Wysocki } 1488be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1489aa42240aSTomasz Figa 14903fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 14913fe57710SJon Hunter { 14923fe57710SJon Hunter struct gpd_link *l, *link; 14933fe57710SJon Hunter 14943fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 14953fe57710SJon Hunter return -EINVAL; 14963fe57710SJon Hunter 149735241d12SLina Iyer genpd_lock(genpd); 14983fe57710SJon Hunter 14993fe57710SJon Hunter if (genpd->has_provider) { 150035241d12SLina Iyer genpd_unlock(genpd); 15013fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 15023fe57710SJon Hunter return -EBUSY; 15033fe57710SJon Hunter } 15043fe57710SJon Hunter 15053fe57710SJon Hunter if (!list_empty(&genpd->master_links) || genpd->device_count) { 150635241d12SLina Iyer genpd_unlock(genpd); 15073fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 15083fe57710SJon Hunter return -EBUSY; 15093fe57710SJon Hunter } 15103fe57710SJon Hunter 15113fe57710SJon Hunter list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { 15123fe57710SJon Hunter list_del(&link->master_node); 15133fe57710SJon Hunter list_del(&link->slave_node); 15143fe57710SJon Hunter kfree(link); 15153fe57710SJon Hunter } 15163fe57710SJon Hunter 15173fe57710SJon Hunter list_del(&genpd->gpd_list_node); 151835241d12SLina Iyer genpd_unlock(genpd); 15193fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 152059d65b73SLina Iyer kfree(genpd->free); 15213fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 15223fe57710SJon Hunter 15233fe57710SJon Hunter return 0; 15243fe57710SJon Hunter } 15253fe57710SJon Hunter 15263fe57710SJon Hunter /** 15273fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 15283fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 15293fe57710SJon Hunter * 15303fe57710SJon Hunter * To remove the PM domain, this function: 15313fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 15323fe57710SJon Hunter * if it was added. 15333fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 15343fe57710SJon Hunter * 15353fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 15363fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 15373fe57710SJon Hunter * devices associated with it. 15383fe57710SJon Hunter */ 15393fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 15403fe57710SJon Hunter { 15413fe57710SJon Hunter int ret; 15423fe57710SJon Hunter 15433fe57710SJon Hunter mutex_lock(&gpd_list_lock); 15443fe57710SJon Hunter ret = genpd_remove(genpd); 15453fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 15463fe57710SJon Hunter 15473fe57710SJon Hunter return ret; 15483fe57710SJon Hunter } 15493fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 15503fe57710SJon Hunter 1551aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1552892ebdccSJon Hunter 1553892ebdccSJon Hunter typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, 1554892ebdccSJon Hunter void *data); 1555892ebdccSJon Hunter 1556aa42240aSTomasz Figa /* 1557aa42240aSTomasz Figa * Device Tree based PM domain providers. 1558aa42240aSTomasz Figa * 1559aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1560aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1561aa42240aSTomasz Figa * 1562aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1563aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1564aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1565aa42240aSTomasz Figa * 1566aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1567892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1568892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 1569aa42240aSTomasz Figa * index. 1570aa42240aSTomasz Figa */ 1571aa42240aSTomasz Figa 1572aa42240aSTomasz Figa /** 1573aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1574aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1575aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1576aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1577aa42240aSTomasz Figa * into a PM domain. 1578aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1579aa42240aSTomasz Figa */ 1580aa42240aSTomasz Figa struct of_genpd_provider { 1581aa42240aSTomasz Figa struct list_head link; 1582aa42240aSTomasz Figa struct device_node *node; 1583aa42240aSTomasz Figa genpd_xlate_t xlate; 1584aa42240aSTomasz Figa void *data; 1585aa42240aSTomasz Figa }; 1586aa42240aSTomasz Figa 1587aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1588aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1589aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1590aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1591aa42240aSTomasz Figa 1592aa42240aSTomasz Figa /** 1593892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 1594aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1595aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1596aa42240aSTomasz Figa * 1597aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1598aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1599aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1600aa42240aSTomasz Figa */ 1601892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 1602aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1603aa42240aSTomasz Figa void *data) 1604aa42240aSTomasz Figa { 1605aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1606aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1607aa42240aSTomasz Figa return data; 1608aa42240aSTomasz Figa } 1609aa42240aSTomasz Figa 1610aa42240aSTomasz Figa /** 1611892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 1612aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1613aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1614aa42240aSTomasz Figa * 1615aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1616aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1617aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1618aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1619aa42240aSTomasz Figa */ 1620892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 1621aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1622aa42240aSTomasz Figa void *data) 1623aa42240aSTomasz Figa { 1624aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1625aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1626aa42240aSTomasz Figa 1627aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1628aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1629aa42240aSTomasz Figa 1630aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1631aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1632aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1633aa42240aSTomasz Figa } 1634aa42240aSTomasz Figa 1635aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1636aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1637aa42240aSTomasz Figa 1638aa42240aSTomasz Figa return genpd_data->domains[idx]; 1639aa42240aSTomasz Figa } 1640aa42240aSTomasz Figa 1641aa42240aSTomasz Figa /** 1642892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 1643aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1644aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1645aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1646aa42240aSTomasz Figa */ 1647892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1648aa42240aSTomasz Figa void *data) 1649aa42240aSTomasz Figa { 1650aa42240aSTomasz Figa struct of_genpd_provider *cp; 1651aa42240aSTomasz Figa 1652aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1653aa42240aSTomasz Figa if (!cp) 1654aa42240aSTomasz Figa return -ENOMEM; 1655aa42240aSTomasz Figa 1656aa42240aSTomasz Figa cp->node = of_node_get(np); 1657aa42240aSTomasz Figa cp->data = data; 1658aa42240aSTomasz Figa cp->xlate = xlate; 1659aa42240aSTomasz Figa 1660aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1661aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1662aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1663aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1664aa42240aSTomasz Figa 1665aa42240aSTomasz Figa return 0; 1666aa42240aSTomasz Figa } 1667892ebdccSJon Hunter 1668892ebdccSJon Hunter /** 1669892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 1670892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1671892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 1672892ebdccSJon Hunter */ 1673892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 1674892ebdccSJon Hunter struct generic_pm_domain *genpd) 1675892ebdccSJon Hunter { 16760159ec67SJon Hunter int ret = -EINVAL; 16770159ec67SJon Hunter 16780159ec67SJon Hunter if (!np || !genpd) 16790159ec67SJon Hunter return -EINVAL; 16800159ec67SJon Hunter 16810159ec67SJon Hunter mutex_lock(&gpd_list_lock); 16820159ec67SJon Hunter 16830159ec67SJon Hunter if (pm_genpd_present(genpd)) 16840159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 16850159ec67SJon Hunter 1686de0aa06dSJon Hunter if (!ret) { 1687de0aa06dSJon Hunter genpd->provider = &np->fwnode; 1688de0aa06dSJon Hunter genpd->has_provider = true; 1689de0aa06dSJon Hunter } 1690de0aa06dSJon Hunter 16910159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 16920159ec67SJon Hunter 16930159ec67SJon Hunter return ret; 1694892ebdccSJon Hunter } 1695892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 1696892ebdccSJon Hunter 1697892ebdccSJon Hunter /** 1698892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 1699892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1700892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 1701892ebdccSJon Hunter */ 1702892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 1703892ebdccSJon Hunter struct genpd_onecell_data *data) 1704892ebdccSJon Hunter { 17050159ec67SJon Hunter unsigned int i; 1706de0aa06dSJon Hunter int ret = -EINVAL; 17070159ec67SJon Hunter 17080159ec67SJon Hunter if (!np || !data) 17090159ec67SJon Hunter return -EINVAL; 17100159ec67SJon Hunter 17110159ec67SJon Hunter mutex_lock(&gpd_list_lock); 17120159ec67SJon Hunter 17130159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 1714609bed67STomeu Vizoso if (!data->domains[i]) 1715609bed67STomeu Vizoso continue; 1716de0aa06dSJon Hunter if (!pm_genpd_present(data->domains[i])) 1717de0aa06dSJon Hunter goto error; 1718de0aa06dSJon Hunter 1719de0aa06dSJon Hunter data->domains[i]->provider = &np->fwnode; 1720de0aa06dSJon Hunter data->domains[i]->has_provider = true; 17210159ec67SJon Hunter } 17220159ec67SJon Hunter 17230159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_onecell, data); 1724de0aa06dSJon Hunter if (ret < 0) 1725de0aa06dSJon Hunter goto error; 1726de0aa06dSJon Hunter 1727de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1728de0aa06dSJon Hunter 1729de0aa06dSJon Hunter return 0; 1730de0aa06dSJon Hunter 1731de0aa06dSJon Hunter error: 1732de0aa06dSJon Hunter while (i--) { 1733609bed67STomeu Vizoso if (!data->domains[i]) 1734609bed67STomeu Vizoso continue; 1735de0aa06dSJon Hunter data->domains[i]->provider = NULL; 1736de0aa06dSJon Hunter data->domains[i]->has_provider = false; 1737de0aa06dSJon Hunter } 17380159ec67SJon Hunter 17390159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 17400159ec67SJon Hunter 17410159ec67SJon Hunter return ret; 1742892ebdccSJon Hunter } 1743892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 1744aa42240aSTomasz Figa 1745aa42240aSTomasz Figa /** 1746aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1747aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1748aa42240aSTomasz Figa */ 1749aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1750aa42240aSTomasz Figa { 1751aa42240aSTomasz Figa struct of_genpd_provider *cp; 1752de0aa06dSJon Hunter struct generic_pm_domain *gpd; 1753aa42240aSTomasz Figa 1754de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 1755aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1756aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1757aa42240aSTomasz Figa if (cp->node == np) { 1758de0aa06dSJon Hunter /* 1759de0aa06dSJon Hunter * For each PM domain associated with the 1760de0aa06dSJon Hunter * provider, set the 'has_provider' to false 1761de0aa06dSJon Hunter * so that the PM domain can be safely removed. 1762de0aa06dSJon Hunter */ 1763de0aa06dSJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) 1764de0aa06dSJon Hunter if (gpd->provider == &np->fwnode) 1765de0aa06dSJon Hunter gpd->has_provider = false; 1766de0aa06dSJon Hunter 1767aa42240aSTomasz Figa list_del(&cp->link); 1768aa42240aSTomasz Figa of_node_put(cp->node); 1769aa42240aSTomasz Figa kfree(cp); 1770aa42240aSTomasz Figa break; 1771aa42240aSTomasz Figa } 1772aa42240aSTomasz Figa } 1773aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1774de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1775aa42240aSTomasz Figa } 1776aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1777aa42240aSTomasz Figa 1778aa42240aSTomasz Figa /** 1779f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 1780aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1781aa42240aSTomasz Figa * 1782aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1783aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1784aa42240aSTomasz Figa * domain. 1785aa42240aSTomasz Figa * 1786aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1787aa42240aSTomasz Figa * on failure. 1788aa42240aSTomasz Figa */ 1789f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 1790aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1791aa42240aSTomasz Figa { 1792aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1793aa42240aSTomasz Figa struct of_genpd_provider *provider; 1794aa42240aSTomasz Figa 179541795a8aSJon Hunter if (!genpdspec) 179641795a8aSJon Hunter return ERR_PTR(-EINVAL); 179741795a8aSJon Hunter 1798aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1799aa42240aSTomasz Figa 1800aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1801aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1802aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1803aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1804aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1805aa42240aSTomasz Figa break; 1806aa42240aSTomasz Figa } 1807aa42240aSTomasz Figa 1808aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1809aa42240aSTomasz Figa 1810aa42240aSTomasz Figa return genpd; 1811aa42240aSTomasz Figa } 1812aa42240aSTomasz Figa 1813aa42240aSTomasz Figa /** 1814ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 1815ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 1816ec69572bSJon Hunter * @dev: Device to be added. 1817ec69572bSJon Hunter * 1818ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 1819ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 1820ec69572bSJon Hunter */ 1821ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 1822ec69572bSJon Hunter { 1823ec69572bSJon Hunter struct generic_pm_domain *genpd; 182419efa5ffSJon Hunter int ret; 182519efa5ffSJon Hunter 182619efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1827ec69572bSJon Hunter 1828f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 182919efa5ffSJon Hunter if (IS_ERR(genpd)) { 183019efa5ffSJon Hunter ret = PTR_ERR(genpd); 183119efa5ffSJon Hunter goto out; 183219efa5ffSJon Hunter } 1833ec69572bSJon Hunter 183419efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, NULL); 183519efa5ffSJon Hunter 183619efa5ffSJon Hunter out: 183719efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 183819efa5ffSJon Hunter 183919efa5ffSJon Hunter return ret; 1840ec69572bSJon Hunter } 1841ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 1842ec69572bSJon Hunter 1843ec69572bSJon Hunter /** 1844ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1845ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 1846ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 1847ec69572bSJon Hunter * 1848ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 1849ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 1850ec69572bSJon Hunter * negative error code on failure. 1851ec69572bSJon Hunter */ 1852ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 1853ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 1854ec69572bSJon Hunter { 1855ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 185619efa5ffSJon Hunter int ret; 185719efa5ffSJon Hunter 185819efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1859ec69572bSJon Hunter 1860f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 186119efa5ffSJon Hunter if (IS_ERR(parent)) { 186219efa5ffSJon Hunter ret = PTR_ERR(parent); 186319efa5ffSJon Hunter goto out; 186419efa5ffSJon Hunter } 1865ec69572bSJon Hunter 1866f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 186719efa5ffSJon Hunter if (IS_ERR(subdomain)) { 186819efa5ffSJon Hunter ret = PTR_ERR(subdomain); 186919efa5ffSJon Hunter goto out; 187019efa5ffSJon Hunter } 1871ec69572bSJon Hunter 187219efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 187319efa5ffSJon Hunter 187419efa5ffSJon Hunter out: 187519efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 187619efa5ffSJon Hunter 187719efa5ffSJon Hunter return ret; 1878ec69572bSJon Hunter } 1879ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 1880ec69572bSJon Hunter 1881ec69572bSJon Hunter /** 188217926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 188317926551SJon Hunter * @provider: Pointer to device structure associated with provider 188417926551SJon Hunter * 188517926551SJon Hunter * Find the last PM domain that was added by a particular provider and 188617926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 188717926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 188817926551SJon Hunter * domain will only be removed, if the provider associated with domain 188917926551SJon Hunter * has been removed. 189017926551SJon Hunter * 189117926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 189217926551SJon Hunter * ERR_PTR() on failure. 189317926551SJon Hunter */ 189417926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 189517926551SJon Hunter { 189617926551SJon Hunter struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT); 189717926551SJon Hunter int ret; 189817926551SJon Hunter 189917926551SJon Hunter if (IS_ERR_OR_NULL(np)) 190017926551SJon Hunter return ERR_PTR(-EINVAL); 190117926551SJon Hunter 190217926551SJon Hunter mutex_lock(&gpd_list_lock); 190317926551SJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 190417926551SJon Hunter if (gpd->provider == &np->fwnode) { 190517926551SJon Hunter ret = genpd_remove(gpd); 190617926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 190717926551SJon Hunter break; 190817926551SJon Hunter } 190917926551SJon Hunter } 191017926551SJon Hunter mutex_unlock(&gpd_list_lock); 191117926551SJon Hunter 191217926551SJon Hunter return genpd; 191317926551SJon Hunter } 191417926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 191517926551SJon Hunter 191617926551SJon Hunter /** 1917aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 19188bb6944eSJon Hunter * @dev: Device to detach. 1919aa42240aSTomasz Figa * @power_off: Currently not used 1920aa42240aSTomasz Figa * 1921aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1922aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1923aa42240aSTomasz Figa */ 1924aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1925aa42240aSTomasz Figa { 1926446d999cSRussell King struct generic_pm_domain *pd; 192793af5e93SGeert Uytterhoeven unsigned int i; 1928aa42240aSTomasz Figa int ret = 0; 1929aa42240aSTomasz Figa 193085168d56SUlf Hansson pd = dev_to_genpd(dev); 193185168d56SUlf Hansson if (IS_ERR(pd)) 1932aa42240aSTomasz Figa return; 1933aa42240aSTomasz Figa 1934aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1935aa42240aSTomasz Figa 193693af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 193785168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 1938aa42240aSTomasz Figa if (ret != -EAGAIN) 1939aa42240aSTomasz Figa break; 194093af5e93SGeert Uytterhoeven 194193af5e93SGeert Uytterhoeven mdelay(i); 1942aa42240aSTomasz Figa cond_resched(); 1943aa42240aSTomasz Figa } 1944aa42240aSTomasz Figa 1945aa42240aSTomasz Figa if (ret < 0) { 1946aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1947aa42240aSTomasz Figa pd->name, ret); 1948aa42240aSTomasz Figa return; 1949aa42240aSTomasz Figa } 1950aa42240aSTomasz Figa 1951aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1952aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1953aa42240aSTomasz Figa } 1954aa42240aSTomasz Figa 1955632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1956632f7ce3SRussell King { 1957632f7ce3SRussell King struct generic_pm_domain *pd; 1958632f7ce3SRussell King 1959632f7ce3SRussell King pd = dev_to_genpd(dev); 1960632f7ce3SRussell King if (IS_ERR(pd)) 1961632f7ce3SRussell King return; 1962632f7ce3SRussell King 1963632f7ce3SRussell King genpd_queue_power_off_work(pd); 1964632f7ce3SRussell King } 1965632f7ce3SRussell King 1966aa42240aSTomasz Figa /** 1967aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1968aa42240aSTomasz Figa * @dev: Device to attach. 1969aa42240aSTomasz Figa * 1970aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1971aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1972aa42240aSTomasz Figa * 1973aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1974aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1975aa42240aSTomasz Figa * 1976311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1977311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1978311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1979311fa6adSJon Hunter * probed and to re-try again later. 1980aa42240aSTomasz Figa */ 1981aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 1982aa42240aSTomasz Figa { 1983aa42240aSTomasz Figa struct of_phandle_args pd_args; 1984aa42240aSTomasz Figa struct generic_pm_domain *pd; 198593af5e93SGeert Uytterhoeven unsigned int i; 1986aa42240aSTomasz Figa int ret; 1987aa42240aSTomasz Figa 1988aa42240aSTomasz Figa if (!dev->of_node) 1989aa42240aSTomasz Figa return -ENODEV; 1990aa42240aSTomasz Figa 1991aa42240aSTomasz Figa if (dev->pm_domain) 1992aa42240aSTomasz Figa return -EEXIST; 1993aa42240aSTomasz Figa 1994aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 1995aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 1996aa42240aSTomasz Figa if (ret < 0) { 1997aa42240aSTomasz Figa if (ret != -ENOENT) 1998aa42240aSTomasz Figa return ret; 1999aa42240aSTomasz Figa 2000aa42240aSTomasz Figa /* 2001aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 2002aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 2003aa42240aSTomasz Figa */ 2004aa42240aSTomasz Figa pd_args.args_count = 0; 2005aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 2006aa42240aSTomasz Figa "samsung,power-domain", 0); 2007aa42240aSTomasz Figa if (!pd_args.np) 2008aa42240aSTomasz Figa return -ENOENT; 2009aa42240aSTomasz Figa } 2010aa42240aSTomasz Figa 201119efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2012f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 2013265e2cf6SEric Anholt of_node_put(pd_args.np); 2014aa42240aSTomasz Figa if (IS_ERR(pd)) { 201519efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2016aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 2017aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 2018311fa6adSJon Hunter return -EPROBE_DEFER; 2019aa42240aSTomasz Figa } 2020aa42240aSTomasz Figa 2021aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 2022aa42240aSTomasz Figa 202393af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 202419efa5ffSJon Hunter ret = genpd_add_device(pd, dev, NULL); 2025aa42240aSTomasz Figa if (ret != -EAGAIN) 2026aa42240aSTomasz Figa break; 202793af5e93SGeert Uytterhoeven 202893af5e93SGeert Uytterhoeven mdelay(i); 2029aa42240aSTomasz Figa cond_resched(); 2030aa42240aSTomasz Figa } 203119efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2032aa42240aSTomasz Figa 2033aa42240aSTomasz Figa if (ret < 0) { 2034aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 2035aa42240aSTomasz Figa pd->name, ret); 2036311fa6adSJon Hunter goto out; 2037aa42240aSTomasz Figa } 2038aa42240aSTomasz Figa 2039aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 2040632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 2041aa42240aSTomasz Figa 204235241d12SLina Iyer genpd_lock(pd); 204353af16f7SUlf Hansson ret = genpd_poweron(pd, 0); 204435241d12SLina Iyer genpd_unlock(pd); 2045311fa6adSJon Hunter out: 2046311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 2047aa42240aSTomasz Figa } 2048aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 204930f60428SLina Iyer 205030f60428SLina Iyer static const struct of_device_id idle_state_match[] = { 205130f60428SLina Iyer { .compatible = "arm,idle-state", }, 205230f60428SLina Iyer { } 205330f60428SLina Iyer }; 205430f60428SLina Iyer 205530f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state, 205630f60428SLina Iyer struct device_node *state_node) 205730f60428SLina Iyer { 205830f60428SLina Iyer int err; 205930f60428SLina Iyer u32 residency; 206030f60428SLina Iyer u32 entry_latency, exit_latency; 206130f60428SLina Iyer const struct of_device_id *match_id; 206230f60428SLina Iyer 206330f60428SLina Iyer match_id = of_match_node(idle_state_match, state_node); 206430f60428SLina Iyer if (!match_id) 206530f60428SLina Iyer return -EINVAL; 206630f60428SLina Iyer 206730f60428SLina Iyer err = of_property_read_u32(state_node, "entry-latency-us", 206830f60428SLina Iyer &entry_latency); 206930f60428SLina Iyer if (err) { 207030f60428SLina Iyer pr_debug(" * %s missing entry-latency-us property\n", 207130f60428SLina Iyer state_node->full_name); 207230f60428SLina Iyer return -EINVAL; 207330f60428SLina Iyer } 207430f60428SLina Iyer 207530f60428SLina Iyer err = of_property_read_u32(state_node, "exit-latency-us", 207630f60428SLina Iyer &exit_latency); 207730f60428SLina Iyer if (err) { 207830f60428SLina Iyer pr_debug(" * %s missing exit-latency-us property\n", 207930f60428SLina Iyer state_node->full_name); 208030f60428SLina Iyer return -EINVAL; 208130f60428SLina Iyer } 208230f60428SLina Iyer 208330f60428SLina Iyer err = of_property_read_u32(state_node, "min-residency-us", &residency); 208430f60428SLina Iyer if (!err) 208530f60428SLina Iyer genpd_state->residency_ns = 1000 * residency; 208630f60428SLina Iyer 208730f60428SLina Iyer genpd_state->power_on_latency_ns = 1000 * exit_latency; 208830f60428SLina Iyer genpd_state->power_off_latency_ns = 1000 * entry_latency; 20890c9b694aSLina Iyer genpd_state->fwnode = &state_node->fwnode; 209030f60428SLina Iyer 209130f60428SLina Iyer return 0; 209230f60428SLina Iyer } 209330f60428SLina Iyer 209430f60428SLina Iyer /** 209530f60428SLina Iyer * of_genpd_parse_idle_states: Return array of idle states for the genpd. 209630f60428SLina Iyer * 209730f60428SLina Iyer * @dn: The genpd device node 209830f60428SLina Iyer * @states: The pointer to which the state array will be saved. 209930f60428SLina Iyer * @n: The count of elements in the array returned from this function. 210030f60428SLina Iyer * 210130f60428SLina Iyer * Returns the device states parsed from the OF node. The memory for the states 210230f60428SLina Iyer * is allocated by this function and is the responsibility of the caller to 210330f60428SLina Iyer * free the memory after use. 210430f60428SLina Iyer */ 210530f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn, 210630f60428SLina Iyer struct genpd_power_state **states, int *n) 210730f60428SLina Iyer { 210830f60428SLina Iyer struct genpd_power_state *st; 210930f60428SLina Iyer struct device_node *np; 211030f60428SLina Iyer int i = 0; 211130f60428SLina Iyer int err, ret; 211230f60428SLina Iyer int count; 211330f60428SLina Iyer struct of_phandle_iterator it; 211430f60428SLina Iyer 211530f60428SLina Iyer count = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 2116a1fee00dSColin Ian King if (count <= 0) 211730f60428SLina Iyer return -EINVAL; 211830f60428SLina Iyer 211930f60428SLina Iyer st = kcalloc(count, sizeof(*st), GFP_KERNEL); 212030f60428SLina Iyer if (!st) 212130f60428SLina Iyer return -ENOMEM; 212230f60428SLina Iyer 212330f60428SLina Iyer /* Loop over the phandles until all the requested entry is found */ 212430f60428SLina Iyer of_for_each_phandle(&it, err, dn, "domain-idle-states", NULL, 0) { 212530f60428SLina Iyer np = it.node; 212630f60428SLina Iyer ret = genpd_parse_state(&st[i++], np); 212730f60428SLina Iyer if (ret) { 212830f60428SLina Iyer pr_err 212930f60428SLina Iyer ("Parsing idle state node %s failed with err %d\n", 213030f60428SLina Iyer np->full_name, ret); 213130f60428SLina Iyer of_node_put(np); 213230f60428SLina Iyer kfree(st); 213330f60428SLina Iyer return ret; 213430f60428SLina Iyer } 213530f60428SLina Iyer } 213630f60428SLina Iyer 213730f60428SLina Iyer *n = count; 213830f60428SLina Iyer *states = st; 213930f60428SLina Iyer 214030f60428SLina Iyer return 0; 214130f60428SLina Iyer } 214230f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 214330f60428SLina Iyer 2144d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 21452bd5306aSMaciej Matraszek 21462bd5306aSMaciej Matraszek 21472bd5306aSMaciej Matraszek /*** debugfs support ***/ 21482bd5306aSMaciej Matraszek 21498b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 21502bd5306aSMaciej Matraszek #include <linux/pm.h> 21512bd5306aSMaciej Matraszek #include <linux/device.h> 21522bd5306aSMaciej Matraszek #include <linux/debugfs.h> 21532bd5306aSMaciej Matraszek #include <linux/seq_file.h> 21542bd5306aSMaciej Matraszek #include <linux/init.h> 21552bd5306aSMaciej Matraszek #include <linux/kobject.h> 21562bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 21572bd5306aSMaciej Matraszek 21582bd5306aSMaciej Matraszek /* 21592bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 2160d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 21612bd5306aSMaciej Matraszek */ 21622bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 21632bd5306aSMaciej Matraszek { 21642bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 21652bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 21662bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 21672bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 21682bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 21692bd5306aSMaciej Matraszek }; 21702bd5306aSMaciej Matraszek const char *p = ""; 21712bd5306aSMaciej Matraszek 21722bd5306aSMaciej Matraszek if (dev->power.runtime_error) 21732bd5306aSMaciej Matraszek p = "error"; 21742bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 21752bd5306aSMaciej Matraszek p = "unsupported"; 21762bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 21772bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 21782bd5306aSMaciej Matraszek else 21792bd5306aSMaciej Matraszek WARN_ON(1); 21802bd5306aSMaciej Matraszek 21812bd5306aSMaciej Matraszek seq_puts(s, p); 21822bd5306aSMaciej Matraszek } 21832bd5306aSMaciej Matraszek 21842bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 218566a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 21862bd5306aSMaciej Matraszek { 21872bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 21882bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 21892bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 21902bd5306aSMaciej Matraszek }; 21912bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 21922bd5306aSMaciej Matraszek const char *kobj_path; 21932bd5306aSMaciej Matraszek struct gpd_link *link; 21946954d432SGeert Uytterhoeven char state[16]; 21952bd5306aSMaciej Matraszek int ret; 21962bd5306aSMaciej Matraszek 219735241d12SLina Iyer ret = genpd_lock_interruptible(genpd); 21982bd5306aSMaciej Matraszek if (ret) 21992bd5306aSMaciej Matraszek return -ERESTARTSYS; 22002bd5306aSMaciej Matraszek 220166a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 22022bd5306aSMaciej Matraszek goto exit; 2203fc5cbf0cSAxel Haslam if (genpd->status == GPD_STATE_POWER_OFF) 22040ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 22056954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 2206fc5cbf0cSAxel Haslam else 22076954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 22086954d432SGeert Uytterhoeven status_lookup[genpd->status]); 22096954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 22102bd5306aSMaciej Matraszek 22112bd5306aSMaciej Matraszek /* 22122bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 22132bd5306aSMaciej Matraszek * master and slave, so we are safe. 221466a5ca4bSKevin Hilman * Also genpd->name is immutable. 22152bd5306aSMaciej Matraszek */ 221666a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 22172bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 221866a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 22192bd5306aSMaciej Matraszek seq_puts(s, ", "); 22202bd5306aSMaciej Matraszek } 22212bd5306aSMaciej Matraszek 222266a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 2223d716f479SLina Iyer kobj_path = kobject_get_path(&pm_data->dev->kobj, 2224d716f479SLina Iyer genpd_is_irq_safe(genpd) ? 2225d716f479SLina Iyer GFP_ATOMIC : GFP_KERNEL); 22262bd5306aSMaciej Matraszek if (kobj_path == NULL) 22272bd5306aSMaciej Matraszek continue; 22282bd5306aSMaciej Matraszek 22292bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 22302bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 22312bd5306aSMaciej Matraszek kfree(kobj_path); 22322bd5306aSMaciej Matraszek } 22332bd5306aSMaciej Matraszek 22342bd5306aSMaciej Matraszek seq_puts(s, "\n"); 22352bd5306aSMaciej Matraszek exit: 223635241d12SLina Iyer genpd_unlock(genpd); 22372bd5306aSMaciej Matraszek 22382bd5306aSMaciej Matraszek return 0; 22392bd5306aSMaciej Matraszek } 22402bd5306aSMaciej Matraszek 22412bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 22422bd5306aSMaciej Matraszek { 224366a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 22442bd5306aSMaciej Matraszek int ret = 0; 22452bd5306aSMaciej Matraszek 22462bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 22472bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 22482bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 22492bd5306aSMaciej Matraszek 22502bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 22512bd5306aSMaciej Matraszek if (ret) 22522bd5306aSMaciej Matraszek return -ERESTARTSYS; 22532bd5306aSMaciej Matraszek 225466a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 225566a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 22562bd5306aSMaciej Matraszek if (ret) 22572bd5306aSMaciej Matraszek break; 22582bd5306aSMaciej Matraszek } 22592bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 22602bd5306aSMaciej Matraszek 22612bd5306aSMaciej Matraszek return ret; 22622bd5306aSMaciej Matraszek } 22632bd5306aSMaciej Matraszek 22642bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 22652bd5306aSMaciej Matraszek { 22662bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 22672bd5306aSMaciej Matraszek } 22682bd5306aSMaciej Matraszek 22692bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 22702bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 22712bd5306aSMaciej Matraszek .read = seq_read, 22722bd5306aSMaciej Matraszek .llseek = seq_lseek, 22732bd5306aSMaciej Matraszek .release = single_release, 22742bd5306aSMaciej Matraszek }; 22752bd5306aSMaciej Matraszek 22762bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 22772bd5306aSMaciej Matraszek { 22782bd5306aSMaciej Matraszek struct dentry *d; 22792bd5306aSMaciej Matraszek 22802bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 22812bd5306aSMaciej Matraszek 22822bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 22832bd5306aSMaciej Matraszek return -ENOMEM; 22842bd5306aSMaciej Matraszek 22852bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 22862bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 22872bd5306aSMaciej Matraszek if (!d) 22882bd5306aSMaciej Matraszek return -ENOMEM; 22892bd5306aSMaciej Matraszek 22902bd5306aSMaciej Matraszek return 0; 22912bd5306aSMaciej Matraszek } 22922bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 22932bd5306aSMaciej Matraszek 22942bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 22952bd5306aSMaciej Matraszek { 22962bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 22972bd5306aSMaciej Matraszek } 22982bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 22998b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 2300