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 12441e2c8e0SUlf Hansson #define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE) 125d716f479SLina Iyer #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) 126d716f479SLina Iyer 127d716f479SLina Iyer static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, 128d716f479SLina Iyer struct generic_pm_domain *genpd) 129d716f479SLina Iyer { 130d716f479SLina Iyer bool ret; 131d716f479SLina Iyer 132d716f479SLina Iyer ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); 133d716f479SLina Iyer 134f3c826acSUlf Hansson /* Warn once if IRQ safe dev in no sleep domain */ 135d716f479SLina Iyer if (ret) 136d716f479SLina Iyer dev_warn_once(dev, "PM domain %s will not be powered off\n", 137d716f479SLina Iyer genpd->name); 138d716f479SLina Iyer 139d716f479SLina Iyer return ret; 140d716f479SLina Iyer } 141d716f479SLina Iyer 142446d999cSRussell King /* 143446d999cSRussell King * Get the generic PM domain for a particular struct device. 144446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 145446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 146446d999cSRussell King * Any failure results in NULL being returned. 147446d999cSRussell King */ 148f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_lookup_dev(struct device *dev) 149446d999cSRussell King { 150446d999cSRussell King struct generic_pm_domain *genpd = NULL, *gpd; 151446d999cSRussell King 152446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 153446d999cSRussell King return NULL; 154446d999cSRussell King 155446d999cSRussell King mutex_lock(&gpd_list_lock); 156446d999cSRussell King list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 157446d999cSRussell King if (&gpd->domain == dev->pm_domain) { 158446d999cSRussell King genpd = gpd; 159446d999cSRussell King break; 160446d999cSRussell King } 161446d999cSRussell King } 162446d999cSRussell King mutex_unlock(&gpd_list_lock); 163446d999cSRussell King 164446d999cSRussell King return genpd; 165446d999cSRussell King } 166446d999cSRussell King 167446d999cSRussell King /* 168446d999cSRussell King * This should only be used where we are certain that the pm_domain 169446d999cSRussell King * attached to the device is a genpd domain. 170446d999cSRussell King */ 171446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 1725248051bSRafael J. Wysocki { 1735248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 1745248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 1755248051bSRafael J. Wysocki 176596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 1775248051bSRafael J. Wysocki } 178f721889fSRafael J. Wysocki 1792b1d88cdSUlf Hansson static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) 180d5e4cbfeSRafael J. Wysocki { 18151cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 182d5e4cbfeSRafael J. Wysocki } 183d5e4cbfeSRafael J. Wysocki 1842b1d88cdSUlf Hansson static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) 185d5e4cbfeSRafael J. Wysocki { 186ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 187d5e4cbfeSRafael J. Wysocki } 188d5e4cbfeSRafael J. Wysocki 189c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 190f721889fSRafael J. Wysocki { 191c4bb3160SRafael J. Wysocki bool ret = false; 192c4bb3160SRafael J. Wysocki 193c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 194c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 195c4bb3160SRafael J. Wysocki 196c4bb3160SRafael J. Wysocki return ret; 197c4bb3160SRafael J. Wysocki } 198c4bb3160SRafael J. Wysocki 199c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 200c4bb3160SRafael J. Wysocki { 201c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 2024e857c58SPeter Zijlstra smp_mb__after_atomic(); 203f721889fSRafael J. Wysocki } 204f721889fSRafael J. Wysocki 20586e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 206c8f0ea45SGeert Uytterhoeven { 207fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 208c8f0ea45SGeert Uytterhoeven ktime_t time_start; 209c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 210c8f0ea45SGeert Uytterhoeven int ret; 211c8f0ea45SGeert Uytterhoeven 212c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 213c8f0ea45SGeert Uytterhoeven return 0; 214c8f0ea45SGeert Uytterhoeven 215a4630c61SGeert Uytterhoeven if (!timed) 216a4630c61SGeert Uytterhoeven return genpd->power_on(genpd); 217a4630c61SGeert Uytterhoeven 218c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 219c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 220c8f0ea45SGeert Uytterhoeven if (ret) 221c8f0ea45SGeert Uytterhoeven return ret; 222c8f0ea45SGeert Uytterhoeven 223c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 224fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 225c8f0ea45SGeert Uytterhoeven return ret; 226c8f0ea45SGeert Uytterhoeven 227fc5cbf0cSAxel Haslam genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 228c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 2296d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 230c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 231c8f0ea45SGeert Uytterhoeven 232c8f0ea45SGeert Uytterhoeven return ret; 233c8f0ea45SGeert Uytterhoeven } 234c8f0ea45SGeert Uytterhoeven 23586e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) 236c8f0ea45SGeert Uytterhoeven { 237fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 238c8f0ea45SGeert Uytterhoeven ktime_t time_start; 239c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 240c8f0ea45SGeert Uytterhoeven int ret; 241c8f0ea45SGeert Uytterhoeven 242c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 243c8f0ea45SGeert Uytterhoeven return 0; 244c8f0ea45SGeert Uytterhoeven 245a4630c61SGeert Uytterhoeven if (!timed) 246a4630c61SGeert Uytterhoeven return genpd->power_off(genpd); 247a4630c61SGeert Uytterhoeven 248c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 249c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 250c8f0ea45SGeert Uytterhoeven if (ret == -EBUSY) 251c8f0ea45SGeert Uytterhoeven return ret; 252c8f0ea45SGeert Uytterhoeven 253c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 254fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 255c8f0ea45SGeert Uytterhoeven return ret; 256c8f0ea45SGeert Uytterhoeven 257fc5cbf0cSAxel Haslam genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 258c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 2596d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 260c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 261c8f0ea45SGeert Uytterhoeven 262c8f0ea45SGeert Uytterhoeven return ret; 263c8f0ea45SGeert Uytterhoeven } 264c8f0ea45SGeert Uytterhoeven 265f721889fSRafael J. Wysocki /** 26686e12eacSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_power_off(). 267a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 26829e47e21SUlf Hansson * 26986e12eacSUlf Hansson * Queue up the execution of genpd_power_off() unless it's already been done 27029e47e21SUlf Hansson * before. 27129e47e21SUlf Hansson */ 27229e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 27329e47e21SUlf Hansson { 27429e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 27529e47e21SUlf Hansson } 27629e47e21SUlf Hansson 27729e47e21SUlf Hansson /** 2781f8728b7SUlf Hansson * genpd_power_off - Remove power from a given PM domain. 2791f8728b7SUlf Hansson * @genpd: PM domain to power down. 2803c64649dSUlf Hansson * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the 2813c64649dSUlf Hansson * RPM status of the releated device is in an intermediate state, not yet turned 2823c64649dSUlf Hansson * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not 2833c64649dSUlf Hansson * be RPM_SUSPENDED, while it tries to power off the PM domain. 2841f8728b7SUlf Hansson * 2851f8728b7SUlf Hansson * If all of the @genpd's devices have been suspended and all of its subdomains 2861f8728b7SUlf Hansson * have been powered down, remove power from @genpd. 2871f8728b7SUlf Hansson */ 2882da83545SUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, 2892da83545SUlf Hansson unsigned int depth) 2901f8728b7SUlf Hansson { 2911f8728b7SUlf Hansson struct pm_domain_data *pdd; 2921f8728b7SUlf Hansson struct gpd_link *link; 2931f8728b7SUlf Hansson unsigned int not_suspended = 0; 2941f8728b7SUlf Hansson 2951f8728b7SUlf Hansson /* 2961f8728b7SUlf Hansson * Do not try to power off the domain in the following situations: 2971f8728b7SUlf Hansson * (1) The domain is already in the "power off" state. 2981f8728b7SUlf Hansson * (2) System suspend is in progress. 2991f8728b7SUlf Hansson */ 30041e2c8e0SUlf Hansson if (!genpd_status_on(genpd) || genpd->prepared_count > 0) 3011f8728b7SUlf Hansson return 0; 3021f8728b7SUlf Hansson 3031f8728b7SUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 3041f8728b7SUlf Hansson return -EBUSY; 3051f8728b7SUlf Hansson 3061f8728b7SUlf Hansson list_for_each_entry(pdd, &genpd->dev_list, list_node) { 3071f8728b7SUlf Hansson enum pm_qos_flags_status stat; 3081f8728b7SUlf Hansson 3091f8728b7SUlf Hansson stat = dev_pm_qos_flags(pdd->dev, 3101f8728b7SUlf Hansson PM_QOS_FLAG_NO_POWER_OFF 3111f8728b7SUlf Hansson | PM_QOS_FLAG_REMOTE_WAKEUP); 3121f8728b7SUlf Hansson if (stat > PM_QOS_FLAGS_NONE) 3131f8728b7SUlf Hansson return -EBUSY; 3141f8728b7SUlf Hansson 3151f8728b7SUlf Hansson /* 3161f8728b7SUlf Hansson * Do not allow PM domain to be powered off, when an IRQ safe 3171f8728b7SUlf Hansson * device is part of a non-IRQ safe domain. 3181f8728b7SUlf Hansson */ 3191f8728b7SUlf Hansson if (!pm_runtime_suspended(pdd->dev) || 3201f8728b7SUlf Hansson irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) 3211f8728b7SUlf Hansson not_suspended++; 3221f8728b7SUlf Hansson } 3231f8728b7SUlf Hansson 3243c64649dSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) 3251f8728b7SUlf Hansson return -EBUSY; 3261f8728b7SUlf Hansson 3271f8728b7SUlf Hansson if (genpd->gov && genpd->gov->power_down_ok) { 3281f8728b7SUlf Hansson if (!genpd->gov->power_down_ok(&genpd->domain)) 3291f8728b7SUlf Hansson return -EAGAIN; 3301f8728b7SUlf Hansson } 3311f8728b7SUlf Hansson 3321f8728b7SUlf Hansson if (genpd->power_off) { 3331f8728b7SUlf Hansson int ret; 3341f8728b7SUlf Hansson 3351f8728b7SUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 3361f8728b7SUlf Hansson return -EBUSY; 3371f8728b7SUlf Hansson 3381f8728b7SUlf Hansson /* 3391f8728b7SUlf Hansson * If sd_count > 0 at this point, one of the subdomains hasn't 3401f8728b7SUlf Hansson * managed to call genpd_power_on() for the master yet after 3411f8728b7SUlf Hansson * incrementing it. In that case genpd_power_on() will wait 3421f8728b7SUlf Hansson * for us to drop the lock, so we can call .power_off() and let 3431f8728b7SUlf Hansson * the genpd_power_on() restore power for us (this shouldn't 3441f8728b7SUlf Hansson * happen very often). 3451f8728b7SUlf Hansson */ 3461f8728b7SUlf Hansson ret = _genpd_power_off(genpd, true); 3471f8728b7SUlf Hansson if (ret) 3481f8728b7SUlf Hansson return ret; 3491f8728b7SUlf Hansson } 3501f8728b7SUlf Hansson 3511f8728b7SUlf Hansson genpd->status = GPD_STATE_POWER_OFF; 3521f8728b7SUlf Hansson 3531f8728b7SUlf Hansson list_for_each_entry(link, &genpd->slave_links, slave_node) { 3541f8728b7SUlf Hansson genpd_sd_counter_dec(link->master); 3552da83545SUlf Hansson genpd_lock_nested(link->master, depth + 1); 3562da83545SUlf Hansson genpd_power_off(link->master, false, depth + 1); 3572da83545SUlf Hansson genpd_unlock(link->master); 3581f8728b7SUlf Hansson } 3591f8728b7SUlf Hansson 3601f8728b7SUlf Hansson return 0; 3611f8728b7SUlf Hansson } 3621f8728b7SUlf Hansson 3631f8728b7SUlf Hansson /** 36486e12eacSUlf Hansson * genpd_power_on - Restore power to a given PM domain and its masters. 3655248051bSRafael J. Wysocki * @genpd: PM domain to power up. 3660106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 3675248051bSRafael J. Wysocki * 3685063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 3695248051bSRafael J. Wysocki * resume a device belonging to it. 3705248051bSRafael J. Wysocki */ 37186e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) 3725248051bSRafael J. Wysocki { 3735063ce15SRafael J. Wysocki struct gpd_link *link; 3745248051bSRafael J. Wysocki int ret = 0; 3755248051bSRafael J. Wysocki 37641e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 3773f241775SRafael J. Wysocki return 0; 3785248051bSRafael J. Wysocki 3795063ce15SRafael J. Wysocki /* 3805063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 3815063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 3825063ce15SRafael J. Wysocki * with it. 3835063ce15SRafael J. Wysocki */ 3845063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 3850106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 3865248051bSRafael J. Wysocki 3870106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 3880106ef51SMarek Szyprowski 38935241d12SLina Iyer genpd_lock_nested(master, depth + 1); 39086e12eacSUlf Hansson ret = genpd_power_on(master, depth + 1); 39135241d12SLina Iyer genpd_unlock(master); 3920106ef51SMarek Szyprowski 3935063ce15SRafael J. Wysocki if (ret) { 3940106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 3959e08cf42SRafael J. Wysocki goto err; 3965248051bSRafael J. Wysocki } 3975063ce15SRafael J. Wysocki } 3985248051bSRafael J. Wysocki 39986e12eacSUlf Hansson ret = _genpd_power_on(genpd, true); 4009e08cf42SRafael J. Wysocki if (ret) 4019e08cf42SRafael J. Wysocki goto err; 4020140d8bdSRafael J. Wysocki 403ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 4043f241775SRafael J. Wysocki return 0; 4059e08cf42SRafael J. Wysocki 4069e08cf42SRafael J. Wysocki err: 40729e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 40829e47e21SUlf Hansson &genpd->slave_links, 40929e47e21SUlf Hansson slave_node) { 4105063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 4112da83545SUlf Hansson genpd_lock_nested(link->master, depth + 1); 4122da83545SUlf Hansson genpd_power_off(link->master, false, depth + 1); 4132da83545SUlf Hansson genpd_unlock(link->master); 41429e47e21SUlf Hansson } 4159e08cf42SRafael J. Wysocki 4163f241775SRafael J. Wysocki return ret; 4173f241775SRafael J. Wysocki } 4183f241775SRafael J. Wysocki 4196ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 4206ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 4216ff7bb0dSRafael J. Wysocki { 4226ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 4236ff7bb0dSRafael J. Wysocki struct device *dev; 4246ff7bb0dSRafael J. Wysocki 4256ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 4266ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 4276ff7bb0dSRafael J. Wysocki 4286ff7bb0dSRafael J. Wysocki for (;;) { 4296ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 4306ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 4316ff7bb0dSRafael J. Wysocki 4326ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 4336ff7bb0dSRafael J. Wysocki 4346ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 4356ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 4361d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 4376ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 4386ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 4396ff7bb0dSRafael J. Wysocki } else { 4406ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 4416ff7bb0dSRafael J. Wysocki } 4426ff7bb0dSRafael J. Wysocki 4436ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 4446ff7bb0dSRafael J. Wysocki 4456ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 44635241d12SLina Iyer genpd_lock(genpd); 4476ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 44835241d12SLina Iyer genpd_unlock(genpd); 4496ff7bb0dSRafael J. Wysocki } 4506ff7bb0dSRafael J. Wysocki 4516ff7bb0dSRafael J. Wysocki dev = dev->parent; 4526ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 4536ff7bb0dSRafael J. Wysocki break; 4546ff7bb0dSRafael J. Wysocki } 4556ff7bb0dSRafael J. Wysocki 4566ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 4576ff7bb0dSRafael J. Wysocki } 4586ff7bb0dSRafael J. Wysocki 4595248051bSRafael J. Wysocki /** 460f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 461f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 462f721889fSRafael J. Wysocki */ 463f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 464f721889fSRafael J. Wysocki { 465f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 466f721889fSRafael J. Wysocki 467f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 468f721889fSRafael J. Wysocki 46935241d12SLina Iyer genpd_lock(genpd); 4702da83545SUlf Hansson genpd_power_off(genpd, false, 0); 47135241d12SLina Iyer genpd_unlock(genpd); 472f721889fSRafael J. Wysocki } 473f721889fSRafael J. Wysocki 474f721889fSRafael J. Wysocki /** 47554eeddbfSUlf Hansson * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 47654eeddbfSUlf Hansson * @dev: Device to handle. 47754eeddbfSUlf Hansson */ 47854eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev) 47954eeddbfSUlf Hansson { 48054eeddbfSUlf Hansson int (*cb)(struct device *__dev); 48154eeddbfSUlf Hansson 48254eeddbfSUlf Hansson if (dev->type && dev->type->pm) 48354eeddbfSUlf Hansson cb = dev->type->pm->runtime_suspend; 48454eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 48554eeddbfSUlf Hansson cb = dev->class->pm->runtime_suspend; 48654eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 48754eeddbfSUlf Hansson cb = dev->bus->pm->runtime_suspend; 48854eeddbfSUlf Hansson else 48954eeddbfSUlf Hansson cb = NULL; 49054eeddbfSUlf Hansson 49154eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 49254eeddbfSUlf Hansson cb = dev->driver->pm->runtime_suspend; 49354eeddbfSUlf Hansson 49454eeddbfSUlf Hansson return cb ? cb(dev) : 0; 49554eeddbfSUlf Hansson } 49654eeddbfSUlf Hansson 49754eeddbfSUlf Hansson /** 49854eeddbfSUlf Hansson * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 49954eeddbfSUlf Hansson * @dev: Device to handle. 50054eeddbfSUlf Hansson */ 50154eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev) 50254eeddbfSUlf Hansson { 50354eeddbfSUlf Hansson int (*cb)(struct device *__dev); 50454eeddbfSUlf Hansson 50554eeddbfSUlf Hansson if (dev->type && dev->type->pm) 50654eeddbfSUlf Hansson cb = dev->type->pm->runtime_resume; 50754eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 50854eeddbfSUlf Hansson cb = dev->class->pm->runtime_resume; 50954eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 51054eeddbfSUlf Hansson cb = dev->bus->pm->runtime_resume; 51154eeddbfSUlf Hansson else 51254eeddbfSUlf Hansson cb = NULL; 51354eeddbfSUlf Hansson 51454eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 51554eeddbfSUlf Hansson cb = dev->driver->pm->runtime_resume; 51654eeddbfSUlf Hansson 51754eeddbfSUlf Hansson return cb ? cb(dev) : 0; 51854eeddbfSUlf Hansson } 51954eeddbfSUlf Hansson 52054eeddbfSUlf Hansson /** 521795bd2e7SUlf Hansson * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 522f721889fSRafael J. Wysocki * @dev: Device to suspend. 523f721889fSRafael J. Wysocki * 524f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 525f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 526f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 527f721889fSRafael J. Wysocki */ 528795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev) 529f721889fSRafael J. Wysocki { 530f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 5319df3921eSUlf Hansson bool (*suspend_ok)(struct device *__dev); 5322b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 533ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 5342b1d88cdSUlf Hansson ktime_t time_start; 5352b1d88cdSUlf Hansson s64 elapsed_ns; 536d5e4cbfeSRafael J. Wysocki int ret; 537f721889fSRafael J. Wysocki 538f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 539f721889fSRafael J. Wysocki 5405248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5415248051bSRafael J. Wysocki if (IS_ERR(genpd)) 542f721889fSRafael J. Wysocki return -EINVAL; 543f721889fSRafael J. Wysocki 544ffe12855SUlf Hansson /* 545ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 546ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 547ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 548ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 549ffe12855SUlf Hansson */ 5509df3921eSUlf Hansson suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 5519df3921eSUlf Hansson if (runtime_pm && suspend_ok && !suspend_ok(dev)) 552b02c999aSRafael J. Wysocki return -EBUSY; 553b02c999aSRafael J. Wysocki 5542b1d88cdSUlf Hansson /* Measure suspend latency. */ 555d33d5a6cSLinus Torvalds time_start = 0; 556ffe12855SUlf Hansson if (runtime_pm) 5572b1d88cdSUlf Hansson time_start = ktime_get(); 5582b1d88cdSUlf Hansson 55954eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 560f721889fSRafael J. Wysocki if (ret) 56117b75ecaSRafael J. Wysocki return ret; 56217b75ecaSRafael J. Wysocki 5632b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 564ba2bbfbfSUlf Hansson if (ret) { 56554eeddbfSUlf Hansson __genpd_runtime_resume(dev); 566ba2bbfbfSUlf Hansson return ret; 567ba2bbfbfSUlf Hansson } 568ba2bbfbfSUlf Hansson 5692b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 570ffe12855SUlf Hansson if (runtime_pm) { 5712b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5722b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 5732b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 5742b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 5752b1d88cdSUlf Hansson elapsed_ns); 5762b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5772b1d88cdSUlf Hansson td->constraint_changed = true; 5782b1d88cdSUlf Hansson } 579ffe12855SUlf Hansson } 5802b1d88cdSUlf Hansson 5810aa2a221SRafael J. Wysocki /* 582d716f479SLina Iyer * If power.irq_safe is set, this routine may be run with 583d716f479SLina Iyer * IRQs disabled, so suspend only if the PM domain also is irq_safe. 5840aa2a221SRafael J. Wysocki */ 585d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) 5860aa2a221SRafael J. Wysocki return 0; 5870aa2a221SRafael J. Wysocki 58835241d12SLina Iyer genpd_lock(genpd); 5892da83545SUlf Hansson genpd_power_off(genpd, true, 0); 59035241d12SLina Iyer genpd_unlock(genpd); 591f721889fSRafael J. Wysocki 592f721889fSRafael J. Wysocki return 0; 593f721889fSRafael J. Wysocki } 594f721889fSRafael J. Wysocki 595f721889fSRafael J. Wysocki /** 596795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 597f721889fSRafael J. Wysocki * @dev: Device to resume. 598f721889fSRafael J. Wysocki * 599f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 600f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 601f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 602f721889fSRafael J. Wysocki */ 603795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 604f721889fSRafael J. Wysocki { 605f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 6062b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 607ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 6082b1d88cdSUlf Hansson ktime_t time_start; 6092b1d88cdSUlf Hansson s64 elapsed_ns; 610f721889fSRafael J. Wysocki int ret; 611ba2bbfbfSUlf Hansson bool timed = true; 612f721889fSRafael J. Wysocki 613f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 614f721889fSRafael J. Wysocki 6155248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6165248051bSRafael J. Wysocki if (IS_ERR(genpd)) 617f721889fSRafael J. Wysocki return -EINVAL; 618f721889fSRafael J. Wysocki 619d716f479SLina Iyer /* 620d716f479SLina Iyer * As we don't power off a non IRQ safe domain, which holds 621d716f479SLina Iyer * an IRQ safe device, we don't need to restore power to it. 622d716f479SLina Iyer */ 623d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) { 624ba2bbfbfSUlf Hansson timed = false; 625ba2bbfbfSUlf Hansson goto out; 626ba2bbfbfSUlf Hansson } 6270aa2a221SRafael J. Wysocki 62835241d12SLina Iyer genpd_lock(genpd); 62986e12eacSUlf Hansson ret = genpd_power_on(genpd, 0); 63035241d12SLina Iyer genpd_unlock(genpd); 631ba2bbfbfSUlf Hansson 632ba2bbfbfSUlf Hansson if (ret) 6333f241775SRafael J. Wysocki return ret; 634c6d22b37SRafael J. Wysocki 635ba2bbfbfSUlf Hansson out: 6362b1d88cdSUlf Hansson /* Measure resume latency. */ 637ab51e6baSAugusto Mecking Caringi time_start = 0; 638ffe12855SUlf Hansson if (timed && runtime_pm) 6392b1d88cdSUlf Hansson time_start = ktime_get(); 6402b1d88cdSUlf Hansson 641076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 642076395caSLaurent Pinchart if (ret) 643076395caSLaurent Pinchart goto err_poweroff; 644076395caSLaurent Pinchart 64554eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 646076395caSLaurent Pinchart if (ret) 647076395caSLaurent Pinchart goto err_stop; 6482b1d88cdSUlf Hansson 6492b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 650ffe12855SUlf Hansson if (timed && runtime_pm) { 6512b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 6522b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 6532b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 6542b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 6552b1d88cdSUlf Hansson elapsed_ns); 6562b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 6572b1d88cdSUlf Hansson td->constraint_changed = true; 6582b1d88cdSUlf Hansson } 6592b1d88cdSUlf Hansson } 66017b75ecaSRafael J. Wysocki 661f721889fSRafael J. Wysocki return 0; 662076395caSLaurent Pinchart 663076395caSLaurent Pinchart err_stop: 664076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 665076395caSLaurent Pinchart err_poweroff: 666d716f479SLina Iyer if (!pm_runtime_is_irq_safe(dev) || 667d716f479SLina Iyer (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { 66835241d12SLina Iyer genpd_lock(genpd); 6692da83545SUlf Hansson genpd_power_off(genpd, true, 0); 67035241d12SLina Iyer genpd_unlock(genpd); 671076395caSLaurent Pinchart } 672076395caSLaurent Pinchart 673076395caSLaurent Pinchart return ret; 674f721889fSRafael J. Wysocki } 675f721889fSRafael J. Wysocki 67639ac5ba5STushar Behera static bool pd_ignore_unused; 67739ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 67839ac5ba5STushar Behera { 67939ac5ba5STushar Behera pd_ignore_unused = true; 68039ac5ba5STushar Behera return 1; 68139ac5ba5STushar Behera } 68239ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 68339ac5ba5STushar Behera 68417f2ae7fSRafael J. Wysocki /** 68586e12eacSUlf Hansson * genpd_power_off_unused - Power off all PM domains with no devices in use. 68617f2ae7fSRafael J. Wysocki */ 68786e12eacSUlf Hansson static int __init genpd_power_off_unused(void) 68817f2ae7fSRafael J. Wysocki { 68917f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 69017f2ae7fSRafael J. Wysocki 69139ac5ba5STushar Behera if (pd_ignore_unused) { 69239ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 693bb4b72fcSUlf Hansson return 0; 69439ac5ba5STushar Behera } 69539ac5ba5STushar Behera 69617f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 69717f2ae7fSRafael J. Wysocki 69817f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 69917f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 70017f2ae7fSRafael J. Wysocki 70117f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 70217f2ae7fSRafael J. Wysocki 7032fe71dcdSUlf Hansson return 0; 7042fe71dcdSUlf Hansson } 70586e12eacSUlf Hansson late_initcall(genpd_power_off_unused); 7062fe71dcdSUlf Hansson 7070159ec67SJon Hunter #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) 708596ba34bSRafael J. Wysocki 70977f827deSRafael J. Wysocki /** 71077f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 71177f827deSRafael J. Wysocki * @genpd: PM domain to check. 71277f827deSRafael J. Wysocki */ 713895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 71477f827deSRafael J. Wysocki { 715895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 71677f827deSRafael J. Wysocki 71777f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 71877f827deSRafael J. Wysocki return false; 71977f827deSRafael J. Wysocki 72077f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 72177f827deSRafael J. Wysocki if (gpd == genpd) 72277f827deSRafael J. Wysocki return true; 72377f827deSRafael J. Wysocki 72477f827deSRafael J. Wysocki return false; 72577f827deSRafael J. Wysocki } 72677f827deSRafael J. Wysocki 7270159ec67SJon Hunter #endif 7280159ec67SJon Hunter 7290159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 7300159ec67SJon Hunter 731d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 732d5e4cbfeSRafael J. Wysocki struct device *dev) 733d5e4cbfeSRafael J. Wysocki { 734d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 735d5e4cbfeSRafael J. Wysocki } 736d5e4cbfeSRafael J. Wysocki 737596ba34bSRafael J. Wysocki /** 73886e12eacSUlf Hansson * genpd_sync_power_off - Synchronously power off a PM domain and its masters. 739596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 7400883ac03SUlf Hansson * @use_lock: use the lock. 7410883ac03SUlf Hansson * @depth: nesting count for lockdep. 742596ba34bSRafael J. Wysocki * 743596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7445063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 745596ba34bSRafael J. Wysocki * 74677f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 7470883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 7480883ac03SUlf Hansson * these cases the lock must be held. 749596ba34bSRafael J. Wysocki */ 7500883ac03SUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, 7510883ac03SUlf Hansson unsigned int depth) 752596ba34bSRafael J. Wysocki { 7535063ce15SRafael J. Wysocki struct gpd_link *link; 754596ba34bSRafael J. Wysocki 75541e2c8e0SUlf Hansson if (!genpd_status_on(genpd)) 756596ba34bSRafael J. Wysocki return; 757596ba34bSRafael J. Wysocki 758c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 759c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 760596ba34bSRafael J. Wysocki return; 761596ba34bSRafael J. Wysocki 762fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 763fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 76486e12eacSUlf Hansson _genpd_power_off(genpd, false); 765596ba34bSRafael J. Wysocki 76617b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7675063ce15SRafael J. Wysocki 7685063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7695063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 7700883ac03SUlf Hansson 7710883ac03SUlf Hansson if (use_lock) 7720883ac03SUlf Hansson genpd_lock_nested(link->master, depth + 1); 7730883ac03SUlf Hansson 7740883ac03SUlf Hansson genpd_sync_power_off(link->master, use_lock, depth + 1); 7750883ac03SUlf Hansson 7760883ac03SUlf Hansson if (use_lock) 7770883ac03SUlf Hansson genpd_unlock(link->master); 778596ba34bSRafael J. Wysocki } 779596ba34bSRafael J. Wysocki } 780596ba34bSRafael J. Wysocki 781596ba34bSRafael J. Wysocki /** 78286e12eacSUlf Hansson * genpd_sync_power_on - Synchronously power on a PM domain and its masters. 783802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 7840883ac03SUlf Hansson * @use_lock: use the lock. 7850883ac03SUlf Hansson * @depth: nesting count for lockdep. 786802d8b49SRafael J. Wysocki * 78777f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 7880883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 7890883ac03SUlf Hansson * these cases the lock must be held. 790802d8b49SRafael J. Wysocki */ 7910883ac03SUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, 7920883ac03SUlf Hansson unsigned int depth) 793802d8b49SRafael J. Wysocki { 794802d8b49SRafael J. Wysocki struct gpd_link *link; 795802d8b49SRafael J. Wysocki 79641e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 797802d8b49SRafael J. Wysocki return; 798802d8b49SRafael J. Wysocki 799802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 800802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 8010883ac03SUlf Hansson 8020883ac03SUlf Hansson if (use_lock) 8030883ac03SUlf Hansson genpd_lock_nested(link->master, depth + 1); 8040883ac03SUlf Hansson 8050883ac03SUlf Hansson genpd_sync_power_on(link->master, use_lock, depth + 1); 8060883ac03SUlf Hansson 8070883ac03SUlf Hansson if (use_lock) 8080883ac03SUlf Hansson genpd_unlock(link->master); 809802d8b49SRafael J. Wysocki } 810802d8b49SRafael J. Wysocki 81186e12eacSUlf Hansson _genpd_power_on(genpd, false); 812802d8b49SRafael J. Wysocki 813802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 814802d8b49SRafael J. Wysocki } 815802d8b49SRafael J. Wysocki 816802d8b49SRafael J. Wysocki /** 8174ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 8184ecd6e65SRafael J. Wysocki * @dev: Device to check. 8194ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 8204ecd6e65SRafael J. Wysocki * 8214ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 8224ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 8234ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 8244ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 8254ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 8264ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 8274ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8284ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8294ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8304ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8314ecd6e65SRafael J. Wysocki */ 8324ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 8334ecd6e65SRafael J. Wysocki { 8344ecd6e65SRafael J. Wysocki bool active_wakeup; 8354ecd6e65SRafael J. Wysocki 8364ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8374ecd6e65SRafael J. Wysocki return false; 8384ecd6e65SRafael J. Wysocki 839d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8404ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8414ecd6e65SRafael J. Wysocki } 8424ecd6e65SRafael J. Wysocki 8434ecd6e65SRafael J. Wysocki /** 844596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 845596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 846596ba34bSRafael J. Wysocki * 847596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 848596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 849596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 850596ba34bSRafael J. Wysocki * consisting of I/O devices. 851596ba34bSRafael J. Wysocki */ 852596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 853596ba34bSRafael J. Wysocki { 854596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 855b6c10c84SRafael J. Wysocki int ret; 856596ba34bSRafael J. Wysocki 857596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 858596ba34bSRafael J. Wysocki 859596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 860596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 861596ba34bSRafael J. Wysocki return -EINVAL; 862596ba34bSRafael J. Wysocki 86317b75ecaSRafael J. Wysocki /* 86417b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 86517b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 86617b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 86717b75ecaSRafael J. Wysocki */ 8684ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8694ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8704ecd6e65SRafael J. Wysocki 87135241d12SLina Iyer genpd_lock(genpd); 872596ba34bSRafael J. Wysocki 87339dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 87465533bbfSRafael J. Wysocki genpd->suspended_count = 0; 87517b75ecaSRafael J. Wysocki 87635241d12SLina Iyer genpd_unlock(genpd); 877596ba34bSRafael J. Wysocki 878b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 879b6c10c84SRafael J. Wysocki if (ret) { 88035241d12SLina Iyer genpd_lock(genpd); 881b6c10c84SRafael J. Wysocki 88239dd0f23SUlf Hansson genpd->prepared_count--; 883b6c10c84SRafael J. Wysocki 88435241d12SLina Iyer genpd_unlock(genpd); 885b6c10c84SRafael J. Wysocki } 88617b75ecaSRafael J. Wysocki 887b6c10c84SRafael J. Wysocki return ret; 888596ba34bSRafael J. Wysocki } 889596ba34bSRafael J. Wysocki 890596ba34bSRafael J. Wysocki /** 8910496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 8920496c8aeSRafael J. Wysocki * @dev: Device to suspend. 8930496c8aeSRafael J. Wysocki * 8940496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 8950496c8aeSRafael J. Wysocki * been stopped. 8960496c8aeSRafael J. Wysocki */ 8970496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 8980496c8aeSRafael J. Wysocki { 8990496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 900122a2237SUlf Hansson int ret; 901596ba34bSRafael J. Wysocki 9020496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9030496c8aeSRafael J. Wysocki 9040496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9050496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9060496c8aeSRafael J. Wysocki return -EINVAL; 9070496c8aeSRafael J. Wysocki 90839dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 909d4f2d87aSRafael J. Wysocki return 0; 910d4f2d87aSRafael J. Wysocki 911122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) { 912122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 913122a2237SUlf Hansson if (ret) 914122a2237SUlf Hansson return ret; 915122a2237SUlf Hansson } 916122a2237SUlf Hansson 9170883ac03SUlf Hansson genpd_lock(genpd); 918596ba34bSRafael J. Wysocki genpd->suspended_count++; 9190883ac03SUlf Hansson genpd_sync_power_off(genpd, true, 0); 9200883ac03SUlf Hansson genpd_unlock(genpd); 921596ba34bSRafael J. Wysocki 922596ba34bSRafael J. Wysocki return 0; 923596ba34bSRafael J. Wysocki } 924596ba34bSRafael J. Wysocki 925596ba34bSRafael J. Wysocki /** 9260496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 927596ba34bSRafael J. Wysocki * @dev: Device to resume. 928596ba34bSRafael J. Wysocki * 9290496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 930596ba34bSRafael J. Wysocki */ 931596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 932596ba34bSRafael J. Wysocki { 933596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 934122a2237SUlf Hansson int ret = 0; 935596ba34bSRafael J. Wysocki 936596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 937596ba34bSRafael J. Wysocki 938596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 939596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 940596ba34bSRafael J. Wysocki return -EINVAL; 941596ba34bSRafael J. Wysocki 94239dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 943596ba34bSRafael J. Wysocki return 0; 944596ba34bSRafael J. Wysocki 9450883ac03SUlf Hansson genpd_lock(genpd); 9460883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 947596ba34bSRafael J. Wysocki genpd->suspended_count--; 9480883ac03SUlf Hansson genpd_unlock(genpd); 949596ba34bSRafael J. Wysocki 950122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 951122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 952122a2237SUlf Hansson 953122a2237SUlf Hansson return ret; 954596ba34bSRafael J. Wysocki } 955596ba34bSRafael J. Wysocki 956596ba34bSRafael J. Wysocki /** 9570496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 958596ba34bSRafael J. Wysocki * @dev: Device to freeze. 959596ba34bSRafael J. Wysocki * 960596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 961596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 962596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 963596ba34bSRafael J. Wysocki * devices. 964596ba34bSRafael J. Wysocki */ 965596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 966596ba34bSRafael J. Wysocki { 967596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 968122a2237SUlf Hansson int ret = 0; 969596ba34bSRafael J. Wysocki 970596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 971596ba34bSRafael J. Wysocki 972596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 973596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 974596ba34bSRafael J. Wysocki return -EINVAL; 975596ba34bSRafael J. Wysocki 976122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 977122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 978122a2237SUlf Hansson 979122a2237SUlf Hansson return ret; 980596ba34bSRafael J. Wysocki } 981596ba34bSRafael J. Wysocki 982596ba34bSRafael J. Wysocki /** 9830496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 984596ba34bSRafael J. Wysocki * @dev: Device to thaw. 985596ba34bSRafael J. Wysocki * 9860496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 9870496c8aeSRafael J. Wysocki * before the system transition. 988596ba34bSRafael J. Wysocki */ 989596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 990596ba34bSRafael J. Wysocki { 991596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 992122a2237SUlf Hansson int ret = 0; 993596ba34bSRafael J. Wysocki 994596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 995596ba34bSRafael J. Wysocki 996596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 997596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 998596ba34bSRafael J. Wysocki return -EINVAL; 999596ba34bSRafael J. Wysocki 1000122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 1001122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 1002122a2237SUlf Hansson 1003122a2237SUlf Hansson return ret; 10040496c8aeSRafael J. Wysocki } 1005596ba34bSRafael J. Wysocki 10060496c8aeSRafael J. Wysocki /** 10070496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1008596ba34bSRafael J. Wysocki * @dev: Device to resume. 1009596ba34bSRafael J. Wysocki * 10100496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 10110496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1012596ba34bSRafael J. Wysocki */ 1013596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1014596ba34bSRafael J. Wysocki { 1015596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1016122a2237SUlf Hansson int ret = 0; 1017596ba34bSRafael J. Wysocki 1018596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1019596ba34bSRafael J. Wysocki 1020596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1021596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1022596ba34bSRafael J. Wysocki return -EINVAL; 1023596ba34bSRafael J. Wysocki 1024596ba34bSRafael J. Wysocki /* 102565533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 102665533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 102765533bbfSRafael J. Wysocki */ 10280883ac03SUlf Hansson genpd_lock(genpd); 102939dd0f23SUlf Hansson if (genpd->suspended_count++ == 0) 103065533bbfSRafael J. Wysocki /* 103165533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 103286e12eacSUlf Hansson * so make it appear as powered off to genpd_sync_power_on(), 1033802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1034596ba34bSRafael J. Wysocki */ 103517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 103618dd2eceSRafael J. Wysocki 10370883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 10380883ac03SUlf Hansson genpd_unlock(genpd); 1039596ba34bSRafael J. Wysocki 1040122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 1041122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 1042122a2237SUlf Hansson 1043122a2237SUlf Hansson return ret; 1044596ba34bSRafael J. Wysocki } 1045596ba34bSRafael J. Wysocki 1046596ba34bSRafael J. Wysocki /** 1047596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1048596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1049596ba34bSRafael J. Wysocki * 1050596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1051596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1052596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1053596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1054596ba34bSRafael J. Wysocki */ 1055596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1056596ba34bSRafael J. Wysocki { 1057596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1058596ba34bSRafael J. Wysocki 1059596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1060596ba34bSRafael J. Wysocki 1061596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1062596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1063596ba34bSRafael J. Wysocki return; 1064596ba34bSRafael J. Wysocki 10654d23a5e8SUlf Hansson pm_generic_complete(dev); 10664d23a5e8SUlf Hansson 106735241d12SLina Iyer genpd_lock(genpd); 1068596ba34bSRafael J. Wysocki 106939dd0f23SUlf Hansson genpd->prepared_count--; 10704d23a5e8SUlf Hansson if (!genpd->prepared_count) 10714d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 1072596ba34bSRafael J. Wysocki 107335241d12SLina Iyer genpd_unlock(genpd); 1074596ba34bSRafael J. Wysocki } 1075596ba34bSRafael J. Wysocki 107677f827deSRafael J. Wysocki /** 1077d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 107877f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 107977f827deSRafael J. Wysocki * 108077f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 108177f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 108277f827deSRafael J. Wysocki */ 1083d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 108477f827deSRafael J. Wysocki { 108577f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 108677f827deSRafael J. Wysocki 108777f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 108877f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 108977f827deSRafael J. Wysocki return; 109077f827deSRafael J. Wysocki 109177f827deSRafael J. Wysocki if (suspend) { 109277f827deSRafael J. Wysocki genpd->suspended_count++; 10930883ac03SUlf Hansson genpd_sync_power_off(genpd, false, 0); 109477f827deSRafael J. Wysocki } else { 10950883ac03SUlf Hansson genpd_sync_power_on(genpd, false, 0); 109677f827deSRafael J. Wysocki genpd->suspended_count--; 109777f827deSRafael J. Wysocki } 109877f827deSRafael J. Wysocki } 1099d47e6464SUlf Hansson 1100d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1101d47e6464SUlf Hansson { 1102d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1103d47e6464SUlf Hansson } 1104d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1105d47e6464SUlf Hansson 1106d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1107d47e6464SUlf Hansson { 1108d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1109d47e6464SUlf Hansson } 1110d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 111177f827deSRafael J. Wysocki 1112d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1113596ba34bSRafael J. Wysocki 1114596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1115596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 1116596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1117596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 1118596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1119596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1120596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1121596ba34bSRafael J. Wysocki 1122596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1123596ba34bSRafael J. Wysocki 1124f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1125f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1126f104e1e5SUlf Hansson struct gpd_timing_data *td) 11271d5fcfecSRafael J. Wysocki { 11281d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11293e235685SUlf Hansson int ret; 11303e235685SUlf Hansson 11313e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11323e235685SUlf Hansson if (ret) 11333e235685SUlf Hansson return ERR_PTR(ret); 11341d5fcfecSRafael J. Wysocki 11351d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 11363e235685SUlf Hansson if (!gpd_data) { 11373e235685SUlf Hansson ret = -ENOMEM; 11383e235685SUlf Hansson goto err_put; 11393e235685SUlf Hansson } 11401d5fcfecSRafael J. Wysocki 1141f104e1e5SUlf Hansson if (td) 1142f104e1e5SUlf Hansson gpd_data->td = *td; 1143f104e1e5SUlf Hansson 1144f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1145f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1146f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1147f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1148f104e1e5SUlf Hansson 1149f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1150f104e1e5SUlf Hansson 1151f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1152f104e1e5SUlf Hansson ret = -EINVAL; 1153f104e1e5SUlf Hansson goto err_free; 1154f104e1e5SUlf Hansson } 1155f104e1e5SUlf Hansson 1156f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1157f104e1e5SUlf Hansson 1158f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1159f104e1e5SUlf Hansson 1160989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1161989561deSTomeu Vizoso 11621d5fcfecSRafael J. Wysocki return gpd_data; 11633e235685SUlf Hansson 1164f104e1e5SUlf Hansson err_free: 1165f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1166f104e1e5SUlf Hansson kfree(gpd_data); 11673e235685SUlf Hansson err_put: 11683e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11693e235685SUlf Hansson return ERR_PTR(ret); 11701d5fcfecSRafael J. Wysocki } 11711d5fcfecSRafael J. Wysocki 117249d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 11731d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 11741d5fcfecSRafael J. Wysocki { 1175989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1176989561deSTomeu Vizoso 1177f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1178f104e1e5SUlf Hansson 1179f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1180f104e1e5SUlf Hansson 1181f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1182f104e1e5SUlf Hansson 11831d5fcfecSRafael J. Wysocki kfree(gpd_data); 11843e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11851d5fcfecSRafael J. Wysocki } 11861d5fcfecSRafael J. Wysocki 118719efa5ffSJon Hunter static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1188b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1189f721889fSRafael J. Wysocki { 1190c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1191f721889fSRafael J. Wysocki int ret = 0; 1192f721889fSRafael J. Wysocki 1193f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1194f721889fSRafael J. Wysocki 1195f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1196f721889fSRafael J. Wysocki return -EINVAL; 1197f721889fSRafael J. Wysocki 1198f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 11993e235685SUlf Hansson if (IS_ERR(gpd_data)) 12003e235685SUlf Hansson return PTR_ERR(gpd_data); 12016ff7bb0dSRafael J. Wysocki 120235241d12SLina Iyer genpd_lock(genpd); 1203f721889fSRafael J. Wysocki 1204596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1205596ba34bSRafael J. Wysocki ret = -EAGAIN; 1206596ba34bSRafael J. Wysocki goto out; 1207596ba34bSRafael J. Wysocki } 1208596ba34bSRafael J. Wysocki 1209b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1210b472c2faSUlf Hansson if (ret) 1211b472c2faSUlf Hansson goto out; 1212d79b6fe1SGeert Uytterhoeven 121314b53064SUlf Hansson genpd->device_count++; 121414b53064SUlf Hansson genpd->max_off_time_changed = true; 121514b53064SUlf Hansson 12161d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 12176ff7bb0dSRafael J. Wysocki 1218f721889fSRafael J. Wysocki out: 121935241d12SLina Iyer genpd_unlock(genpd); 1220f721889fSRafael J. Wysocki 1221c0356db7SUlf Hansson if (ret) 1222c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1223c0356db7SUlf Hansson else 1224c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12251d5fcfecSRafael J. Wysocki 1226f721889fSRafael J. Wysocki return ret; 1227f721889fSRafael J. Wysocki } 122819efa5ffSJon Hunter 122919efa5ffSJon Hunter /** 123019efa5ffSJon Hunter * __pm_genpd_add_device - Add a device to an I/O PM domain. 123119efa5ffSJon Hunter * @genpd: PM domain to add the device to. 123219efa5ffSJon Hunter * @dev: Device to be added. 123319efa5ffSJon Hunter * @td: Set of PM QoS timing parameters to attach to the device. 123419efa5ffSJon Hunter */ 123519efa5ffSJon Hunter int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 123619efa5ffSJon Hunter struct gpd_timing_data *td) 123719efa5ffSJon Hunter { 123819efa5ffSJon Hunter int ret; 123919efa5ffSJon Hunter 124019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 124119efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, td); 124219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 124319efa5ffSJon Hunter 124419efa5ffSJon Hunter return ret; 124519efa5ffSJon Hunter } 124624c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1247f721889fSRafael J. Wysocki 124885168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1249f721889fSRafael J. Wysocki struct device *dev) 1250f721889fSRafael J. Wysocki { 12516ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12524605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1253efa69025SRafael J. Wysocki int ret = 0; 1254f721889fSRafael J. Wysocki 1255f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1256f721889fSRafael J. Wysocki 1257c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1258c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1259c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1260c0356db7SUlf Hansson 126135241d12SLina Iyer genpd_lock(genpd); 1262f721889fSRafael J. Wysocki 1263596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1264596ba34bSRafael J. Wysocki ret = -EAGAIN; 1265596ba34bSRafael J. Wysocki goto out; 1266596ba34bSRafael J. Wysocki } 1267596ba34bSRafael J. Wysocki 12686ff7bb0dSRafael J. Wysocki genpd->device_count--; 12696ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 12706ff7bb0dSRafael J. Wysocki 1271d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1272c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1273d79b6fe1SGeert Uytterhoeven 1274efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 12756ff7bb0dSRafael J. Wysocki 127635241d12SLina Iyer genpd_unlock(genpd); 12776ff7bb0dSRafael J. Wysocki 127849d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 12791d5fcfecSRafael J. Wysocki 12806ff7bb0dSRafael J. Wysocki return 0; 1281f721889fSRafael J. Wysocki 1282596ba34bSRafael J. Wysocki out: 128335241d12SLina Iyer genpd_unlock(genpd); 1284c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1285f721889fSRafael J. Wysocki 1286f721889fSRafael J. Wysocki return ret; 1287f721889fSRafael J. Wysocki } 128885168d56SUlf Hansson 128985168d56SUlf Hansson /** 129085168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 129185168d56SUlf Hansson * @genpd: PM domain to remove the device from. 129285168d56SUlf Hansson * @dev: Device to be removed. 129385168d56SUlf Hansson */ 129485168d56SUlf Hansson int pm_genpd_remove_device(struct generic_pm_domain *genpd, 129585168d56SUlf Hansson struct device *dev) 129685168d56SUlf Hansson { 129785168d56SUlf Hansson if (!genpd || genpd != genpd_lookup_dev(dev)) 129885168d56SUlf Hansson return -EINVAL; 129985168d56SUlf Hansson 130085168d56SUlf Hansson return genpd_remove_device(genpd, dev); 130185168d56SUlf Hansson } 130224c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1303f721889fSRafael J. Wysocki 130419efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1305bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1306f721889fSRafael J. Wysocki { 13072547923dSLina Iyer struct gpd_link *link, *itr; 1308f721889fSRafael J. Wysocki int ret = 0; 1309f721889fSRafael J. Wysocki 1310fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1311fb7268beSRafael J. Wysocki || genpd == subdomain) 1312f721889fSRafael J. Wysocki return -EINVAL; 1313f721889fSRafael J. Wysocki 1314d716f479SLina Iyer /* 1315d716f479SLina Iyer * If the domain can be powered on/off in an IRQ safe 1316d716f479SLina Iyer * context, ensure that the subdomain can also be 1317d716f479SLina Iyer * powered on/off in that context. 1318d716f479SLina Iyer */ 1319d716f479SLina Iyer if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 132044cae7d5SDan Carpenter WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 1321d716f479SLina Iyer genpd->name, subdomain->name); 1322d716f479SLina Iyer return -EINVAL; 1323d716f479SLina Iyer } 1324d716f479SLina Iyer 13252547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13262547923dSLina Iyer if (!link) 13272547923dSLina Iyer return -ENOMEM; 13282547923dSLina Iyer 132935241d12SLina Iyer genpd_lock(subdomain); 133035241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1331f721889fSRafael J. Wysocki 133241e2c8e0SUlf Hansson if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) { 1333f721889fSRafael J. Wysocki ret = -EINVAL; 1334f721889fSRafael J. Wysocki goto out; 1335f721889fSRafael J. Wysocki } 1336f721889fSRafael J. Wysocki 13372547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 13382547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1339f721889fSRafael J. Wysocki ret = -EINVAL; 1340f721889fSRafael J. Wysocki goto out; 1341f721889fSRafael J. Wysocki } 1342f721889fSRafael J. Wysocki } 1343f721889fSRafael J. Wysocki 13445063ce15SRafael J. Wysocki link->master = genpd; 13455063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1346bc0403ffSRafael J. Wysocki link->slave = subdomain; 1347bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 134841e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1349c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1350f721889fSRafael J. Wysocki 1351f721889fSRafael J. Wysocki out: 135235241d12SLina Iyer genpd_unlock(genpd); 135335241d12SLina Iyer genpd_unlock(subdomain); 13542547923dSLina Iyer if (ret) 13552547923dSLina Iyer kfree(link); 1356f721889fSRafael J. Wysocki return ret; 1357f721889fSRafael J. Wysocki } 135819efa5ffSJon Hunter 135919efa5ffSJon Hunter /** 136019efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 136119efa5ffSJon Hunter * @genpd: Master PM domain to add the subdomain to. 136219efa5ffSJon Hunter * @subdomain: Subdomain to be added. 136319efa5ffSJon Hunter */ 136419efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 136519efa5ffSJon Hunter struct generic_pm_domain *subdomain) 136619efa5ffSJon Hunter { 136719efa5ffSJon Hunter int ret; 136819efa5ffSJon Hunter 136919efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 137019efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 137119efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 137219efa5ffSJon Hunter 137319efa5ffSJon Hunter return ret; 137419efa5ffSJon Hunter } 1375d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1376f721889fSRafael J. Wysocki 1377f721889fSRafael J. Wysocki /** 1378f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1379f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 13805063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1381f721889fSRafael J. Wysocki */ 1382f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 13835063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1384f721889fSRafael J. Wysocki { 13855063ce15SRafael J. Wysocki struct gpd_link *link; 1386f721889fSRafael J. Wysocki int ret = -EINVAL; 1387f721889fSRafael J. Wysocki 13885063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1389f721889fSRafael J. Wysocki return -EINVAL; 1390f721889fSRafael J. Wysocki 139135241d12SLina Iyer genpd_lock(subdomain); 139235241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1393f721889fSRafael J. Wysocki 1394beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 139530e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 139630e7a65bSJon Hunter subdomain->name); 139730e7a65bSJon Hunter ret = -EBUSY; 139830e7a65bSJon Hunter goto out; 139930e7a65bSJon Hunter } 140030e7a65bSJon Hunter 14015063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 14025063ce15SRafael J. Wysocki if (link->slave != subdomain) 1403f721889fSRafael J. Wysocki continue; 1404f721889fSRafael J. Wysocki 14055063ce15SRafael J. Wysocki list_del(&link->master_node); 14065063ce15SRafael J. Wysocki list_del(&link->slave_node); 14075063ce15SRafael J. Wysocki kfree(link); 140841e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1409f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1410f721889fSRafael J. Wysocki 1411f721889fSRafael J. Wysocki ret = 0; 1412f721889fSRafael J. Wysocki break; 1413f721889fSRafael J. Wysocki } 1414f721889fSRafael J. Wysocki 141530e7a65bSJon Hunter out: 141635241d12SLina Iyer genpd_unlock(genpd); 141735241d12SLina Iyer genpd_unlock(subdomain); 1418f721889fSRafael J. Wysocki 1419f721889fSRafael J. Wysocki return ret; 1420f721889fSRafael J. Wysocki } 1421d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1422f721889fSRafael J. Wysocki 142359d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd) 142459d65b73SLina Iyer { 142559d65b73SLina Iyer struct genpd_power_state *state; 142659d65b73SLina Iyer 142759d65b73SLina Iyer state = kzalloc(sizeof(*state), GFP_KERNEL); 142859d65b73SLina Iyer if (!state) 142959d65b73SLina Iyer return -ENOMEM; 143059d65b73SLina Iyer 143159d65b73SLina Iyer genpd->states = state; 143259d65b73SLina Iyer genpd->state_count = 1; 143359d65b73SLina Iyer genpd->free = state; 143459d65b73SLina Iyer 143559d65b73SLina Iyer return 0; 143659d65b73SLina Iyer } 143759d65b73SLina Iyer 1438d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd) 1439d716f479SLina Iyer { 1440d716f479SLina Iyer if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 1441d716f479SLina Iyer spin_lock_init(&genpd->slock); 1442d716f479SLina Iyer genpd->lock_ops = &genpd_spin_ops; 1443d716f479SLina Iyer } else { 1444d716f479SLina Iyer mutex_init(&genpd->mlock); 1445d716f479SLina Iyer genpd->lock_ops = &genpd_mtx_ops; 1446d716f479SLina Iyer } 1447d716f479SLina Iyer } 1448d716f479SLina Iyer 1449d23b9b00SRafael J. Wysocki /** 1450f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1451f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1452f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1453f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 14547eb231c3SUlf Hansson * 14557eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1456f721889fSRafael J. Wysocki */ 14577eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1458f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1459f721889fSRafael J. Wysocki { 146059d65b73SLina Iyer int ret; 146159d65b73SLina Iyer 1462f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 14637eb231c3SUlf Hansson return -EINVAL; 1464f721889fSRafael J. Wysocki 14655063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 14665063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1467f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1468d716f479SLina Iyer genpd_lock_init(genpd); 1469f721889fSRafael J. Wysocki genpd->gov = gov; 1470f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1471c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 147217b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1473596ba34bSRafael J. Wysocki genpd->device_count = 0; 1474221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 14756ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1476de0aa06dSJon Hunter genpd->provider = NULL; 1477de0aa06dSJon Hunter genpd->has_provider = false; 1478795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1479795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 1480596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1481596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1482596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1483596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1484596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1485d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1486596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1487596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1488c11f6f5bSUlf Hansson 1489c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1490c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1491c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1492c11f6f5bSUlf Hansson } 1493c11f6f5bSUlf Hansson 1494fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 149559d65b73SLina Iyer if (genpd->state_count == 0) { 149659d65b73SLina Iyer ret = genpd_set_default_power_state(genpd); 149759d65b73SLina Iyer if (ret) 149859d65b73SLina Iyer return ret; 149959d65b73SLina Iyer } 1500fc5cbf0cSAxel Haslam 15015125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 15025125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 15035125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 15047eb231c3SUlf Hansson 15057eb231c3SUlf Hansson return 0; 15065125bbf3SRafael J. Wysocki } 1507be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1508aa42240aSTomasz Figa 15093fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 15103fe57710SJon Hunter { 15113fe57710SJon Hunter struct gpd_link *l, *link; 15123fe57710SJon Hunter 15133fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 15143fe57710SJon Hunter return -EINVAL; 15153fe57710SJon Hunter 151635241d12SLina Iyer genpd_lock(genpd); 15173fe57710SJon Hunter 15183fe57710SJon Hunter if (genpd->has_provider) { 151935241d12SLina Iyer genpd_unlock(genpd); 15203fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 15213fe57710SJon Hunter return -EBUSY; 15223fe57710SJon Hunter } 15233fe57710SJon Hunter 15243fe57710SJon Hunter if (!list_empty(&genpd->master_links) || genpd->device_count) { 152535241d12SLina Iyer genpd_unlock(genpd); 15263fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 15273fe57710SJon Hunter return -EBUSY; 15283fe57710SJon Hunter } 15293fe57710SJon Hunter 15303fe57710SJon Hunter list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { 15313fe57710SJon Hunter list_del(&link->master_node); 15323fe57710SJon Hunter list_del(&link->slave_node); 15333fe57710SJon Hunter kfree(link); 15343fe57710SJon Hunter } 15353fe57710SJon Hunter 15363fe57710SJon Hunter list_del(&genpd->gpd_list_node); 153735241d12SLina Iyer genpd_unlock(genpd); 15383fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 153959d65b73SLina Iyer kfree(genpd->free); 15403fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 15413fe57710SJon Hunter 15423fe57710SJon Hunter return 0; 15433fe57710SJon Hunter } 15443fe57710SJon Hunter 15453fe57710SJon Hunter /** 15463fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 15473fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 15483fe57710SJon Hunter * 15493fe57710SJon Hunter * To remove the PM domain, this function: 15503fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 15513fe57710SJon Hunter * if it was added. 15523fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 15533fe57710SJon Hunter * 15543fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 15553fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 15563fe57710SJon Hunter * devices associated with it. 15573fe57710SJon Hunter */ 15583fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 15593fe57710SJon Hunter { 15603fe57710SJon Hunter int ret; 15613fe57710SJon Hunter 15623fe57710SJon Hunter mutex_lock(&gpd_list_lock); 15633fe57710SJon Hunter ret = genpd_remove(genpd); 15643fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 15653fe57710SJon Hunter 15663fe57710SJon Hunter return ret; 15673fe57710SJon Hunter } 15683fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 15693fe57710SJon Hunter 1570aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1571892ebdccSJon Hunter 1572892ebdccSJon Hunter typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, 1573892ebdccSJon Hunter void *data); 1574892ebdccSJon Hunter 1575aa42240aSTomasz Figa /* 1576aa42240aSTomasz Figa * Device Tree based PM domain providers. 1577aa42240aSTomasz Figa * 1578aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1579aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1580aa42240aSTomasz Figa * 1581aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1582aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1583aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1584aa42240aSTomasz Figa * 1585aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1586892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1587892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 1588aa42240aSTomasz Figa * index. 1589aa42240aSTomasz Figa */ 1590aa42240aSTomasz Figa 1591aa42240aSTomasz Figa /** 1592aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1593aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1594aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1595aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1596aa42240aSTomasz Figa * into a PM domain. 1597aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1598aa42240aSTomasz Figa */ 1599aa42240aSTomasz Figa struct of_genpd_provider { 1600aa42240aSTomasz Figa struct list_head link; 1601aa42240aSTomasz Figa struct device_node *node; 1602aa42240aSTomasz Figa genpd_xlate_t xlate; 1603aa42240aSTomasz Figa void *data; 1604aa42240aSTomasz Figa }; 1605aa42240aSTomasz Figa 1606aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1607aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1608aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1609aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1610aa42240aSTomasz Figa 1611aa42240aSTomasz Figa /** 1612892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 1613aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1614aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1615aa42240aSTomasz Figa * 1616aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1617aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1618aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1619aa42240aSTomasz Figa */ 1620892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 1621aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1622aa42240aSTomasz Figa void *data) 1623aa42240aSTomasz Figa { 1624aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1625aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1626aa42240aSTomasz Figa return data; 1627aa42240aSTomasz Figa } 1628aa42240aSTomasz Figa 1629aa42240aSTomasz Figa /** 1630892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 1631aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1632aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1633aa42240aSTomasz Figa * 1634aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1635aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1636aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1637aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1638aa42240aSTomasz Figa */ 1639892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 1640aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1641aa42240aSTomasz Figa void *data) 1642aa42240aSTomasz Figa { 1643aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1644aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1645aa42240aSTomasz Figa 1646aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1647aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1648aa42240aSTomasz Figa 1649aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1650aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1651aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1652aa42240aSTomasz Figa } 1653aa42240aSTomasz Figa 1654aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1655aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1656aa42240aSTomasz Figa 1657aa42240aSTomasz Figa return genpd_data->domains[idx]; 1658aa42240aSTomasz Figa } 1659aa42240aSTomasz Figa 1660aa42240aSTomasz Figa /** 1661892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 1662aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1663aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1664aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1665aa42240aSTomasz Figa */ 1666892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1667aa42240aSTomasz Figa void *data) 1668aa42240aSTomasz Figa { 1669aa42240aSTomasz Figa struct of_genpd_provider *cp; 1670aa42240aSTomasz Figa 1671aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1672aa42240aSTomasz Figa if (!cp) 1673aa42240aSTomasz Figa return -ENOMEM; 1674aa42240aSTomasz Figa 1675aa42240aSTomasz Figa cp->node = of_node_get(np); 1676aa42240aSTomasz Figa cp->data = data; 1677aa42240aSTomasz Figa cp->xlate = xlate; 1678aa42240aSTomasz Figa 1679aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1680aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1681aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1682aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1683aa42240aSTomasz Figa 1684aa42240aSTomasz Figa return 0; 1685aa42240aSTomasz Figa } 1686892ebdccSJon Hunter 1687892ebdccSJon Hunter /** 1688892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 1689892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1690892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 1691892ebdccSJon Hunter */ 1692892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 1693892ebdccSJon Hunter struct generic_pm_domain *genpd) 1694892ebdccSJon Hunter { 16950159ec67SJon Hunter int ret = -EINVAL; 16960159ec67SJon Hunter 16970159ec67SJon Hunter if (!np || !genpd) 16980159ec67SJon Hunter return -EINVAL; 16990159ec67SJon Hunter 17000159ec67SJon Hunter mutex_lock(&gpd_list_lock); 17010159ec67SJon Hunter 17028ce95844SViresh Kumar if (pm_genpd_present(genpd)) { 17030159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 1704de0aa06dSJon Hunter if (!ret) { 1705de0aa06dSJon Hunter genpd->provider = &np->fwnode; 1706de0aa06dSJon Hunter genpd->has_provider = true; 1707de0aa06dSJon Hunter } 17088ce95844SViresh Kumar } 1709de0aa06dSJon Hunter 17100159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 17110159ec67SJon Hunter 17120159ec67SJon Hunter return ret; 1713892ebdccSJon Hunter } 1714892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 1715892ebdccSJon Hunter 1716892ebdccSJon Hunter /** 1717892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 1718892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1719892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 1720892ebdccSJon Hunter */ 1721892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 1722892ebdccSJon Hunter struct genpd_onecell_data *data) 1723892ebdccSJon Hunter { 17240159ec67SJon Hunter unsigned int i; 1725de0aa06dSJon Hunter int ret = -EINVAL; 17260159ec67SJon Hunter 17270159ec67SJon Hunter if (!np || !data) 17280159ec67SJon Hunter return -EINVAL; 17290159ec67SJon Hunter 17300159ec67SJon Hunter mutex_lock(&gpd_list_lock); 17310159ec67SJon Hunter 17320159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 1733609bed67STomeu Vizoso if (!data->domains[i]) 1734609bed67STomeu Vizoso continue; 1735de0aa06dSJon Hunter if (!pm_genpd_present(data->domains[i])) 1736de0aa06dSJon Hunter goto error; 1737de0aa06dSJon Hunter 1738de0aa06dSJon Hunter data->domains[i]->provider = &np->fwnode; 1739de0aa06dSJon Hunter data->domains[i]->has_provider = true; 17400159ec67SJon Hunter } 17410159ec67SJon Hunter 17420159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_onecell, data); 1743de0aa06dSJon Hunter if (ret < 0) 1744de0aa06dSJon Hunter goto error; 1745de0aa06dSJon Hunter 1746de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1747de0aa06dSJon Hunter 1748de0aa06dSJon Hunter return 0; 1749de0aa06dSJon Hunter 1750de0aa06dSJon Hunter error: 1751de0aa06dSJon Hunter while (i--) { 1752609bed67STomeu Vizoso if (!data->domains[i]) 1753609bed67STomeu Vizoso continue; 1754de0aa06dSJon Hunter data->domains[i]->provider = NULL; 1755de0aa06dSJon Hunter data->domains[i]->has_provider = false; 1756de0aa06dSJon Hunter } 17570159ec67SJon Hunter 17580159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 17590159ec67SJon Hunter 17600159ec67SJon Hunter return ret; 1761892ebdccSJon Hunter } 1762892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 1763aa42240aSTomasz Figa 1764aa42240aSTomasz Figa /** 1765aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1766aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1767aa42240aSTomasz Figa */ 1768aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1769aa42240aSTomasz Figa { 1770aa42240aSTomasz Figa struct of_genpd_provider *cp; 1771de0aa06dSJon Hunter struct generic_pm_domain *gpd; 1772aa42240aSTomasz Figa 1773de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 1774aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1775aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1776aa42240aSTomasz Figa if (cp->node == np) { 1777de0aa06dSJon Hunter /* 1778de0aa06dSJon Hunter * For each PM domain associated with the 1779de0aa06dSJon Hunter * provider, set the 'has_provider' to false 1780de0aa06dSJon Hunter * so that the PM domain can be safely removed. 1781de0aa06dSJon Hunter */ 1782de0aa06dSJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) 1783de0aa06dSJon Hunter if (gpd->provider == &np->fwnode) 1784de0aa06dSJon Hunter gpd->has_provider = false; 1785de0aa06dSJon Hunter 1786aa42240aSTomasz Figa list_del(&cp->link); 1787aa42240aSTomasz Figa of_node_put(cp->node); 1788aa42240aSTomasz Figa kfree(cp); 1789aa42240aSTomasz Figa break; 1790aa42240aSTomasz Figa } 1791aa42240aSTomasz Figa } 1792aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1793de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1794aa42240aSTomasz Figa } 1795aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1796aa42240aSTomasz Figa 1797aa42240aSTomasz Figa /** 1798f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 1799aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1800aa42240aSTomasz Figa * 1801aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1802aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1803aa42240aSTomasz Figa * domain. 1804aa42240aSTomasz Figa * 1805aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1806aa42240aSTomasz Figa * on failure. 1807aa42240aSTomasz Figa */ 1808f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 1809aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1810aa42240aSTomasz Figa { 1811aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1812aa42240aSTomasz Figa struct of_genpd_provider *provider; 1813aa42240aSTomasz Figa 181441795a8aSJon Hunter if (!genpdspec) 181541795a8aSJon Hunter return ERR_PTR(-EINVAL); 181641795a8aSJon Hunter 1817aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1818aa42240aSTomasz Figa 1819aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1820aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1821aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1822aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1823aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1824aa42240aSTomasz Figa break; 1825aa42240aSTomasz Figa } 1826aa42240aSTomasz Figa 1827aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1828aa42240aSTomasz Figa 1829aa42240aSTomasz Figa return genpd; 1830aa42240aSTomasz Figa } 1831aa42240aSTomasz Figa 1832aa42240aSTomasz Figa /** 1833ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 1834ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 1835ec69572bSJon Hunter * @dev: Device to be added. 1836ec69572bSJon Hunter * 1837ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 1838ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 1839ec69572bSJon Hunter */ 1840ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 1841ec69572bSJon Hunter { 1842ec69572bSJon Hunter struct generic_pm_domain *genpd; 184319efa5ffSJon Hunter int ret; 184419efa5ffSJon Hunter 184519efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1846ec69572bSJon Hunter 1847f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 184819efa5ffSJon Hunter if (IS_ERR(genpd)) { 184919efa5ffSJon Hunter ret = PTR_ERR(genpd); 185019efa5ffSJon Hunter goto out; 185119efa5ffSJon Hunter } 1852ec69572bSJon Hunter 185319efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, NULL); 185419efa5ffSJon Hunter 185519efa5ffSJon Hunter out: 185619efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 185719efa5ffSJon Hunter 185819efa5ffSJon Hunter return ret; 1859ec69572bSJon Hunter } 1860ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 1861ec69572bSJon Hunter 1862ec69572bSJon Hunter /** 1863ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1864ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 1865ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 1866ec69572bSJon Hunter * 1867ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 1868ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 1869ec69572bSJon Hunter * negative error code on failure. 1870ec69572bSJon Hunter */ 1871ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 1872ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 1873ec69572bSJon Hunter { 1874ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 187519efa5ffSJon Hunter int ret; 187619efa5ffSJon Hunter 187719efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1878ec69572bSJon Hunter 1879f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 188019efa5ffSJon Hunter if (IS_ERR(parent)) { 188119efa5ffSJon Hunter ret = PTR_ERR(parent); 188219efa5ffSJon Hunter goto out; 188319efa5ffSJon Hunter } 1884ec69572bSJon Hunter 1885f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 188619efa5ffSJon Hunter if (IS_ERR(subdomain)) { 188719efa5ffSJon Hunter ret = PTR_ERR(subdomain); 188819efa5ffSJon Hunter goto out; 188919efa5ffSJon Hunter } 1890ec69572bSJon Hunter 189119efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 189219efa5ffSJon Hunter 189319efa5ffSJon Hunter out: 189419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 189519efa5ffSJon Hunter 189619efa5ffSJon Hunter return ret; 1897ec69572bSJon Hunter } 1898ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 1899ec69572bSJon Hunter 1900ec69572bSJon Hunter /** 190117926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 190217926551SJon Hunter * @provider: Pointer to device structure associated with provider 190317926551SJon Hunter * 190417926551SJon Hunter * Find the last PM domain that was added by a particular provider and 190517926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 190617926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 190717926551SJon Hunter * domain will only be removed, if the provider associated with domain 190817926551SJon Hunter * has been removed. 190917926551SJon Hunter * 191017926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 191117926551SJon Hunter * ERR_PTR() on failure. 191217926551SJon Hunter */ 191317926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 191417926551SJon Hunter { 191517926551SJon Hunter struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT); 191617926551SJon Hunter int ret; 191717926551SJon Hunter 191817926551SJon Hunter if (IS_ERR_OR_NULL(np)) 191917926551SJon Hunter return ERR_PTR(-EINVAL); 192017926551SJon Hunter 192117926551SJon Hunter mutex_lock(&gpd_list_lock); 192217926551SJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 192317926551SJon Hunter if (gpd->provider == &np->fwnode) { 192417926551SJon Hunter ret = genpd_remove(gpd); 192517926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 192617926551SJon Hunter break; 192717926551SJon Hunter } 192817926551SJon Hunter } 192917926551SJon Hunter mutex_unlock(&gpd_list_lock); 193017926551SJon Hunter 193117926551SJon Hunter return genpd; 193217926551SJon Hunter } 193317926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 193417926551SJon Hunter 193517926551SJon Hunter /** 1936aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 19378bb6944eSJon Hunter * @dev: Device to detach. 1938aa42240aSTomasz Figa * @power_off: Currently not used 1939aa42240aSTomasz Figa * 1940aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1941aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1942aa42240aSTomasz Figa */ 1943aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1944aa42240aSTomasz Figa { 1945446d999cSRussell King struct generic_pm_domain *pd; 194693af5e93SGeert Uytterhoeven unsigned int i; 1947aa42240aSTomasz Figa int ret = 0; 1948aa42240aSTomasz Figa 194985168d56SUlf Hansson pd = dev_to_genpd(dev); 195085168d56SUlf Hansson if (IS_ERR(pd)) 1951aa42240aSTomasz Figa return; 1952aa42240aSTomasz Figa 1953aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1954aa42240aSTomasz Figa 195593af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 195685168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 1957aa42240aSTomasz Figa if (ret != -EAGAIN) 1958aa42240aSTomasz Figa break; 195993af5e93SGeert Uytterhoeven 196093af5e93SGeert Uytterhoeven mdelay(i); 1961aa42240aSTomasz Figa cond_resched(); 1962aa42240aSTomasz Figa } 1963aa42240aSTomasz Figa 1964aa42240aSTomasz Figa if (ret < 0) { 1965aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1966aa42240aSTomasz Figa pd->name, ret); 1967aa42240aSTomasz Figa return; 1968aa42240aSTomasz Figa } 1969aa42240aSTomasz Figa 1970aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1971aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1972aa42240aSTomasz Figa } 1973aa42240aSTomasz Figa 1974632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1975632f7ce3SRussell King { 1976632f7ce3SRussell King struct generic_pm_domain *pd; 1977632f7ce3SRussell King 1978632f7ce3SRussell King pd = dev_to_genpd(dev); 1979632f7ce3SRussell King if (IS_ERR(pd)) 1980632f7ce3SRussell King return; 1981632f7ce3SRussell King 1982632f7ce3SRussell King genpd_queue_power_off_work(pd); 1983632f7ce3SRussell King } 1984632f7ce3SRussell King 1985aa42240aSTomasz Figa /** 1986aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1987aa42240aSTomasz Figa * @dev: Device to attach. 1988aa42240aSTomasz Figa * 1989aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1990aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1991aa42240aSTomasz Figa * 1992aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1993aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1994aa42240aSTomasz Figa * 1995311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1996311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1997311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1998311fa6adSJon Hunter * probed and to re-try again later. 1999aa42240aSTomasz Figa */ 2000aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 2001aa42240aSTomasz Figa { 2002aa42240aSTomasz Figa struct of_phandle_args pd_args; 2003aa42240aSTomasz Figa struct generic_pm_domain *pd; 200493af5e93SGeert Uytterhoeven unsigned int i; 2005aa42240aSTomasz Figa int ret; 2006aa42240aSTomasz Figa 2007aa42240aSTomasz Figa if (!dev->of_node) 2008aa42240aSTomasz Figa return -ENODEV; 2009aa42240aSTomasz Figa 2010aa42240aSTomasz Figa if (dev->pm_domain) 2011aa42240aSTomasz Figa return -EEXIST; 2012aa42240aSTomasz Figa 2013aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 2014aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 2015aa42240aSTomasz Figa if (ret < 0) { 2016aa42240aSTomasz Figa if (ret != -ENOENT) 2017aa42240aSTomasz Figa return ret; 2018aa42240aSTomasz Figa 2019aa42240aSTomasz Figa /* 2020aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 2021aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 2022aa42240aSTomasz Figa */ 2023aa42240aSTomasz Figa pd_args.args_count = 0; 2024aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 2025aa42240aSTomasz Figa "samsung,power-domain", 0); 2026aa42240aSTomasz Figa if (!pd_args.np) 2027aa42240aSTomasz Figa return -ENOENT; 2028aa42240aSTomasz Figa } 2029aa42240aSTomasz Figa 203019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2031f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 2032265e2cf6SEric Anholt of_node_put(pd_args.np); 2033aa42240aSTomasz Figa if (IS_ERR(pd)) { 203419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2035aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 2036aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 2037311fa6adSJon Hunter return -EPROBE_DEFER; 2038aa42240aSTomasz Figa } 2039aa42240aSTomasz Figa 2040aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 2041aa42240aSTomasz Figa 204293af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 204319efa5ffSJon Hunter ret = genpd_add_device(pd, dev, NULL); 2044aa42240aSTomasz Figa if (ret != -EAGAIN) 2045aa42240aSTomasz Figa break; 204693af5e93SGeert Uytterhoeven 204793af5e93SGeert Uytterhoeven mdelay(i); 2048aa42240aSTomasz Figa cond_resched(); 2049aa42240aSTomasz Figa } 205019efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2051aa42240aSTomasz Figa 2052aa42240aSTomasz Figa if (ret < 0) { 205334994692SGeert Uytterhoeven if (ret != -EPROBE_DEFER) 2054aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 2055aa42240aSTomasz Figa pd->name, ret); 2056311fa6adSJon Hunter goto out; 2057aa42240aSTomasz Figa } 2058aa42240aSTomasz Figa 2059aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 2060632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 2061aa42240aSTomasz Figa 206235241d12SLina Iyer genpd_lock(pd); 206386e12eacSUlf Hansson ret = genpd_power_on(pd, 0); 206435241d12SLina Iyer genpd_unlock(pd); 2065311fa6adSJon Hunter out: 2066311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 2067aa42240aSTomasz Figa } 2068aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 206930f60428SLina Iyer 207030f60428SLina Iyer static const struct of_device_id idle_state_match[] = { 2071598da548SLina Iyer { .compatible = "domain-idle-state", }, 207230f60428SLina Iyer { } 207330f60428SLina Iyer }; 207430f60428SLina Iyer 207530f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state, 207630f60428SLina Iyer struct device_node *state_node) 207730f60428SLina Iyer { 207830f60428SLina Iyer int err; 207930f60428SLina Iyer u32 residency; 208030f60428SLina Iyer u32 entry_latency, exit_latency; 208130f60428SLina Iyer const struct of_device_id *match_id; 208230f60428SLina Iyer 208330f60428SLina Iyer match_id = of_match_node(idle_state_match, state_node); 208430f60428SLina Iyer if (!match_id) 208530f60428SLina Iyer return -EINVAL; 208630f60428SLina Iyer 208730f60428SLina Iyer err = of_property_read_u32(state_node, "entry-latency-us", 208830f60428SLina Iyer &entry_latency); 208930f60428SLina Iyer if (err) { 209030f60428SLina Iyer pr_debug(" * %s missing entry-latency-us property\n", 209130f60428SLina Iyer state_node->full_name); 209230f60428SLina Iyer return -EINVAL; 209330f60428SLina Iyer } 209430f60428SLina Iyer 209530f60428SLina Iyer err = of_property_read_u32(state_node, "exit-latency-us", 209630f60428SLina Iyer &exit_latency); 209730f60428SLina Iyer if (err) { 209830f60428SLina Iyer pr_debug(" * %s missing exit-latency-us property\n", 209930f60428SLina Iyer state_node->full_name); 210030f60428SLina Iyer return -EINVAL; 210130f60428SLina Iyer } 210230f60428SLina Iyer 210330f60428SLina Iyer err = of_property_read_u32(state_node, "min-residency-us", &residency); 210430f60428SLina Iyer if (!err) 210530f60428SLina Iyer genpd_state->residency_ns = 1000 * residency; 210630f60428SLina Iyer 210730f60428SLina Iyer genpd_state->power_on_latency_ns = 1000 * exit_latency; 210830f60428SLina Iyer genpd_state->power_off_latency_ns = 1000 * entry_latency; 21090c9b694aSLina Iyer genpd_state->fwnode = &state_node->fwnode; 211030f60428SLina Iyer 211130f60428SLina Iyer return 0; 211230f60428SLina Iyer } 211330f60428SLina Iyer 211430f60428SLina Iyer /** 211530f60428SLina Iyer * of_genpd_parse_idle_states: Return array of idle states for the genpd. 211630f60428SLina Iyer * 211730f60428SLina Iyer * @dn: The genpd device node 211830f60428SLina Iyer * @states: The pointer to which the state array will be saved. 211930f60428SLina Iyer * @n: The count of elements in the array returned from this function. 212030f60428SLina Iyer * 212130f60428SLina Iyer * Returns the device states parsed from the OF node. The memory for the states 212230f60428SLina Iyer * is allocated by this function and is the responsibility of the caller to 212330f60428SLina Iyer * free the memory after use. 212430f60428SLina Iyer */ 212530f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn, 212630f60428SLina Iyer struct genpd_power_state **states, int *n) 212730f60428SLina Iyer { 212830f60428SLina Iyer struct genpd_power_state *st; 212930f60428SLina Iyer struct device_node *np; 213030f60428SLina Iyer int i = 0; 213130f60428SLina Iyer int err, ret; 213230f60428SLina Iyer int count; 213330f60428SLina Iyer struct of_phandle_iterator it; 213430f60428SLina Iyer 213530f60428SLina Iyer count = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 2136a1fee00dSColin Ian King if (count <= 0) 213730f60428SLina Iyer return -EINVAL; 213830f60428SLina Iyer 213930f60428SLina Iyer st = kcalloc(count, sizeof(*st), GFP_KERNEL); 214030f60428SLina Iyer if (!st) 214130f60428SLina Iyer return -ENOMEM; 214230f60428SLina Iyer 214330f60428SLina Iyer /* Loop over the phandles until all the requested entry is found */ 214430f60428SLina Iyer of_for_each_phandle(&it, err, dn, "domain-idle-states", NULL, 0) { 214530f60428SLina Iyer np = it.node; 214630f60428SLina Iyer ret = genpd_parse_state(&st[i++], np); 214730f60428SLina Iyer if (ret) { 214830f60428SLina Iyer pr_err 214930f60428SLina Iyer ("Parsing idle state node %s failed with err %d\n", 215030f60428SLina Iyer np->full_name, ret); 215130f60428SLina Iyer of_node_put(np); 215230f60428SLina Iyer kfree(st); 215330f60428SLina Iyer return ret; 215430f60428SLina Iyer } 215530f60428SLina Iyer } 215630f60428SLina Iyer 215730f60428SLina Iyer *n = count; 215830f60428SLina Iyer *states = st; 215930f60428SLina Iyer 216030f60428SLina Iyer return 0; 216130f60428SLina Iyer } 216230f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 216330f60428SLina Iyer 2164d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 21652bd5306aSMaciej Matraszek 21662bd5306aSMaciej Matraszek 21672bd5306aSMaciej Matraszek /*** debugfs support ***/ 21682bd5306aSMaciej Matraszek 21698b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 21702bd5306aSMaciej Matraszek #include <linux/pm.h> 21712bd5306aSMaciej Matraszek #include <linux/device.h> 21722bd5306aSMaciej Matraszek #include <linux/debugfs.h> 21732bd5306aSMaciej Matraszek #include <linux/seq_file.h> 21742bd5306aSMaciej Matraszek #include <linux/init.h> 21752bd5306aSMaciej Matraszek #include <linux/kobject.h> 21762bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 21772bd5306aSMaciej Matraszek 21782bd5306aSMaciej Matraszek /* 21792bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 2180d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 21812bd5306aSMaciej Matraszek */ 21822bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 21832bd5306aSMaciej Matraszek { 21842bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 21852bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 21862bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 21872bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 21882bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 21892bd5306aSMaciej Matraszek }; 21902bd5306aSMaciej Matraszek const char *p = ""; 21912bd5306aSMaciej Matraszek 21922bd5306aSMaciej Matraszek if (dev->power.runtime_error) 21932bd5306aSMaciej Matraszek p = "error"; 21942bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 21952bd5306aSMaciej Matraszek p = "unsupported"; 21962bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 21972bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 21982bd5306aSMaciej Matraszek else 21992bd5306aSMaciej Matraszek WARN_ON(1); 22002bd5306aSMaciej Matraszek 22012bd5306aSMaciej Matraszek seq_puts(s, p); 22022bd5306aSMaciej Matraszek } 22032bd5306aSMaciej Matraszek 22042bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 220566a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 22062bd5306aSMaciej Matraszek { 22072bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 22082bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 22092bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 22102bd5306aSMaciej Matraszek }; 22112bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 22122bd5306aSMaciej Matraszek const char *kobj_path; 22132bd5306aSMaciej Matraszek struct gpd_link *link; 22146954d432SGeert Uytterhoeven char state[16]; 22152bd5306aSMaciej Matraszek int ret; 22162bd5306aSMaciej Matraszek 221735241d12SLina Iyer ret = genpd_lock_interruptible(genpd); 22182bd5306aSMaciej Matraszek if (ret) 22192bd5306aSMaciej Matraszek return -ERESTARTSYS; 22202bd5306aSMaciej Matraszek 222166a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 22222bd5306aSMaciej Matraszek goto exit; 222341e2c8e0SUlf Hansson if (!genpd_status_on(genpd)) 22240ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 22256954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 2226fc5cbf0cSAxel Haslam else 22276954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 22286954d432SGeert Uytterhoeven status_lookup[genpd->status]); 22296954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 22302bd5306aSMaciej Matraszek 22312bd5306aSMaciej Matraszek /* 22322bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 22332bd5306aSMaciej Matraszek * master and slave, so we are safe. 223466a5ca4bSKevin Hilman * Also genpd->name is immutable. 22352bd5306aSMaciej Matraszek */ 223666a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 22372bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 223866a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 22392bd5306aSMaciej Matraszek seq_puts(s, ", "); 22402bd5306aSMaciej Matraszek } 22412bd5306aSMaciej Matraszek 224266a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 2243d716f479SLina Iyer kobj_path = kobject_get_path(&pm_data->dev->kobj, 2244d716f479SLina Iyer genpd_is_irq_safe(genpd) ? 2245d716f479SLina Iyer GFP_ATOMIC : GFP_KERNEL); 22462bd5306aSMaciej Matraszek if (kobj_path == NULL) 22472bd5306aSMaciej Matraszek continue; 22482bd5306aSMaciej Matraszek 22492bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 22502bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 22512bd5306aSMaciej Matraszek kfree(kobj_path); 22522bd5306aSMaciej Matraszek } 22532bd5306aSMaciej Matraszek 22542bd5306aSMaciej Matraszek seq_puts(s, "\n"); 22552bd5306aSMaciej Matraszek exit: 225635241d12SLina Iyer genpd_unlock(genpd); 22572bd5306aSMaciej Matraszek 22582bd5306aSMaciej Matraszek return 0; 22592bd5306aSMaciej Matraszek } 22602bd5306aSMaciej Matraszek 22612bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 22622bd5306aSMaciej Matraszek { 226366a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 22642bd5306aSMaciej Matraszek int ret = 0; 22652bd5306aSMaciej Matraszek 22662bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 22672bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 22682bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 22692bd5306aSMaciej Matraszek 22702bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 22712bd5306aSMaciej Matraszek if (ret) 22722bd5306aSMaciej Matraszek return -ERESTARTSYS; 22732bd5306aSMaciej Matraszek 227466a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 227566a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 22762bd5306aSMaciej Matraszek if (ret) 22772bd5306aSMaciej Matraszek break; 22782bd5306aSMaciej Matraszek } 22792bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 22802bd5306aSMaciej Matraszek 22812bd5306aSMaciej Matraszek return ret; 22822bd5306aSMaciej Matraszek } 22832bd5306aSMaciej Matraszek 22842bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 22852bd5306aSMaciej Matraszek { 22862bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 22872bd5306aSMaciej Matraszek } 22882bd5306aSMaciej Matraszek 22892bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 22902bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 22912bd5306aSMaciej Matraszek .read = seq_read, 22922bd5306aSMaciej Matraszek .llseek = seq_lseek, 22932bd5306aSMaciej Matraszek .release = single_release, 22942bd5306aSMaciej Matraszek }; 22952bd5306aSMaciej Matraszek 22962bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 22972bd5306aSMaciej Matraszek { 22982bd5306aSMaciej Matraszek struct dentry *d; 22992bd5306aSMaciej Matraszek 23002bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 23012bd5306aSMaciej Matraszek 23022bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 23032bd5306aSMaciej Matraszek return -ENOMEM; 23042bd5306aSMaciej Matraszek 23052bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 23062bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 23072bd5306aSMaciej Matraszek if (!d) 23082bd5306aSMaciej Matraszek return -ENOMEM; 23092bd5306aSMaciej Matraszek 23102bd5306aSMaciej Matraszek return 0; 23112bd5306aSMaciej Matraszek } 23122bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 23132bd5306aSMaciej Matraszek 23142bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 23152bd5306aSMaciej Matraszek { 23162bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 23172bd5306aSMaciej Matraszek } 23182bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 23198b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 2320