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) 126ffaa42e8SUlf Hansson #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) 127d716f479SLina Iyer 128d716f479SLina Iyer static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, 129d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd) 130d716f479SLina Iyer { 131d716f479SLina Iyer bool ret; 132d716f479SLina Iyer 133d716f479SLina Iyer ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); 134d716f479SLina Iyer 135075c37d5SUlf Hansson /* 136075c37d5SUlf Hansson * Warn once if an IRQ safe device is attached to a no sleep domain, as 137075c37d5SUlf Hansson * to indicate a suboptimal configuration for PM. For an always on 138075c37d5SUlf Hansson * domain this isn't case, thus don't warn. 139075c37d5SUlf Hansson */ 140075c37d5SUlf Hansson if (ret && !genpd_is_always_on(genpd)) 141d716f479SLina Iyer dev_warn_once(dev, "PM domain %s will not be powered off\n", 142d716f479SLina Iyer genpd->name); 143d716f479SLina Iyer 144d716f479SLina Iyer return ret; 145d716f479SLina Iyer } 146d716f479SLina Iyer 147446d999cSRussell King /* 148446d999cSRussell King * Get the generic PM domain for a particular struct device. 149446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 150446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 151446d999cSRussell King * Any failure results in NULL being returned. 152446d999cSRussell King */ 153f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_lookup_dev(struct device *dev) 154446d999cSRussell King { 155446d999cSRussell King struct generic_pm_domain *genpd = NULL, *gpd; 156446d999cSRussell King 157446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 158446d999cSRussell King return NULL; 159446d999cSRussell King 160446d999cSRussell King mutex_lock(&gpd_list_lock); 161446d999cSRussell King list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 162446d999cSRussell King if (&gpd->domain == dev->pm_domain) { 163446d999cSRussell King genpd = gpd; 164446d999cSRussell King break; 165446d999cSRussell King } 166446d999cSRussell King } 167446d999cSRussell King mutex_unlock(&gpd_list_lock); 168446d999cSRussell King 169446d999cSRussell King return genpd; 170446d999cSRussell King } 171446d999cSRussell King 172446d999cSRussell King /* 173446d999cSRussell King * This should only be used where we are certain that the pm_domain 174446d999cSRussell King * attached to the device is a genpd domain. 175446d999cSRussell King */ 176446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 1775248051bSRafael J. Wysocki { 1785248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 1795248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 1805248051bSRafael J. Wysocki 181596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 1825248051bSRafael J. Wysocki } 183f721889fSRafael J. Wysocki 184d8600c8bSKrzysztof Kozlowski static int genpd_stop_dev(const struct generic_pm_domain *genpd, 185d8600c8bSKrzysztof Kozlowski struct device *dev) 186d5e4cbfeSRafael J. Wysocki { 18751cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 188d5e4cbfeSRafael J. Wysocki } 189d5e4cbfeSRafael J. Wysocki 190d8600c8bSKrzysztof Kozlowski static int genpd_start_dev(const struct generic_pm_domain *genpd, 191d8600c8bSKrzysztof Kozlowski struct device *dev) 192d5e4cbfeSRafael J. Wysocki { 193ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 194d5e4cbfeSRafael J. Wysocki } 195d5e4cbfeSRafael J. Wysocki 196c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 197f721889fSRafael J. Wysocki { 198c4bb3160SRafael J. Wysocki bool ret = false; 199c4bb3160SRafael J. Wysocki 200c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 201c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 202c4bb3160SRafael J. Wysocki 203c4bb3160SRafael J. Wysocki return ret; 204c4bb3160SRafael J. Wysocki } 205c4bb3160SRafael J. Wysocki 206c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 207c4bb3160SRafael J. Wysocki { 208c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 2094e857c58SPeter Zijlstra smp_mb__after_atomic(); 210f721889fSRafael J. Wysocki } 211f721889fSRafael J. Wysocki 21286e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 213c8f0ea45SGeert Uytterhoeven { 214fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 215c8f0ea45SGeert Uytterhoeven ktime_t time_start; 216c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 217c8f0ea45SGeert Uytterhoeven int ret; 218c8f0ea45SGeert Uytterhoeven 219c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 220c8f0ea45SGeert Uytterhoeven return 0; 221c8f0ea45SGeert Uytterhoeven 222a4630c61SGeert Uytterhoeven if (!timed) 223a4630c61SGeert Uytterhoeven return genpd->power_on(genpd); 224a4630c61SGeert Uytterhoeven 225c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 226c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 227c8f0ea45SGeert Uytterhoeven if (ret) 228c8f0ea45SGeert Uytterhoeven return ret; 229c8f0ea45SGeert Uytterhoeven 230c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 231fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 232c8f0ea45SGeert Uytterhoeven return ret; 233c8f0ea45SGeert Uytterhoeven 234fc5cbf0cSAxel Haslam genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 235c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 2366d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 237c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 238c8f0ea45SGeert Uytterhoeven 239c8f0ea45SGeert Uytterhoeven return ret; 240c8f0ea45SGeert Uytterhoeven } 241c8f0ea45SGeert Uytterhoeven 24286e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) 243c8f0ea45SGeert Uytterhoeven { 244fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 245c8f0ea45SGeert Uytterhoeven ktime_t time_start; 246c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 247c8f0ea45SGeert Uytterhoeven int ret; 248c8f0ea45SGeert Uytterhoeven 249c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 250c8f0ea45SGeert Uytterhoeven return 0; 251c8f0ea45SGeert Uytterhoeven 252a4630c61SGeert Uytterhoeven if (!timed) 253a4630c61SGeert Uytterhoeven return genpd->power_off(genpd); 254a4630c61SGeert Uytterhoeven 255c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 256c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 257c8f0ea45SGeert Uytterhoeven if (ret == -EBUSY) 258c8f0ea45SGeert Uytterhoeven return ret; 259c8f0ea45SGeert Uytterhoeven 260c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 261fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 262c8f0ea45SGeert Uytterhoeven return ret; 263c8f0ea45SGeert Uytterhoeven 264fc5cbf0cSAxel Haslam genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 265c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 2666d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 267c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 268c8f0ea45SGeert Uytterhoeven 269c8f0ea45SGeert Uytterhoeven return ret; 270c8f0ea45SGeert Uytterhoeven } 271c8f0ea45SGeert Uytterhoeven 272f721889fSRafael J. Wysocki /** 27386e12eacSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_power_off(). 274a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 27529e47e21SUlf Hansson * 27686e12eacSUlf Hansson * Queue up the execution of genpd_power_off() unless it's already been done 27729e47e21SUlf Hansson * before. 27829e47e21SUlf Hansson */ 27929e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 28029e47e21SUlf Hansson { 28129e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 28229e47e21SUlf Hansson } 28329e47e21SUlf Hansson 28429e47e21SUlf Hansson /** 2851f8728b7SUlf Hansson * genpd_power_off - Remove power from a given PM domain. 2861f8728b7SUlf Hansson * @genpd: PM domain to power down. 2873c64649dSUlf Hansson * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the 2883c64649dSUlf Hansson * RPM status of the releated device is in an intermediate state, not yet turned 2893c64649dSUlf Hansson * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not 2903c64649dSUlf Hansson * be RPM_SUSPENDED, while it tries to power off the PM domain. 2911f8728b7SUlf Hansson * 2921f8728b7SUlf Hansson * If all of the @genpd's devices have been suspended and all of its subdomains 2931f8728b7SUlf Hansson * have been powered down, remove power from @genpd. 2941f8728b7SUlf Hansson */ 2952da83545SUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, 2962da83545SUlf Hansson unsigned int depth) 2971f8728b7SUlf Hansson { 2981f8728b7SUlf Hansson struct pm_domain_data *pdd; 2991f8728b7SUlf Hansson struct gpd_link *link; 3001f8728b7SUlf Hansson unsigned int not_suspended = 0; 3011f8728b7SUlf Hansson 3021f8728b7SUlf Hansson /* 3031f8728b7SUlf Hansson * Do not try to power off the domain in the following situations: 3041f8728b7SUlf Hansson * (1) The domain is already in the "power off" state. 3051f8728b7SUlf Hansson * (2) System suspend is in progress. 3061f8728b7SUlf Hansson */ 30741e2c8e0SUlf Hansson if (!genpd_status_on(genpd) || genpd->prepared_count > 0) 3081f8728b7SUlf Hansson return 0; 3091f8728b7SUlf Hansson 310ffaa42e8SUlf Hansson /* 311ffaa42e8SUlf Hansson * Abort power off for the PM domain in the following situations: 312ffaa42e8SUlf Hansson * (1) The domain is configured as always on. 313ffaa42e8SUlf Hansson * (2) When the domain has a subdomain being powered on. 314ffaa42e8SUlf Hansson */ 315ffaa42e8SUlf Hansson if (genpd_is_always_on(genpd) || atomic_read(&genpd->sd_count) > 0) 3161f8728b7SUlf Hansson return -EBUSY; 3171f8728b7SUlf Hansson 3181f8728b7SUlf Hansson list_for_each_entry(pdd, &genpd->dev_list, list_node) { 3191f8728b7SUlf Hansson enum pm_qos_flags_status stat; 3201f8728b7SUlf Hansson 3211f8728b7SUlf Hansson stat = dev_pm_qos_flags(pdd->dev, 3221f8728b7SUlf Hansson PM_QOS_FLAG_NO_POWER_OFF 3231f8728b7SUlf Hansson | PM_QOS_FLAG_REMOTE_WAKEUP); 3241f8728b7SUlf Hansson if (stat > PM_QOS_FLAGS_NONE) 3251f8728b7SUlf Hansson return -EBUSY; 3261f8728b7SUlf Hansson 3271f8728b7SUlf Hansson /* 3281f8728b7SUlf Hansson * Do not allow PM domain to be powered off, when an IRQ safe 3291f8728b7SUlf Hansson * device is part of a non-IRQ safe domain. 3301f8728b7SUlf Hansson */ 3311f8728b7SUlf Hansson if (!pm_runtime_suspended(pdd->dev) || 3321f8728b7SUlf Hansson irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) 3331f8728b7SUlf Hansson not_suspended++; 3341f8728b7SUlf Hansson } 3351f8728b7SUlf Hansson 3363c64649dSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) 3371f8728b7SUlf Hansson return -EBUSY; 3381f8728b7SUlf Hansson 3391f8728b7SUlf Hansson if (genpd->gov && genpd->gov->power_down_ok) { 3401f8728b7SUlf Hansson if (!genpd->gov->power_down_ok(&genpd->domain)) 3411f8728b7SUlf Hansson return -EAGAIN; 3421f8728b7SUlf Hansson } 3431f8728b7SUlf Hansson 3441f8728b7SUlf Hansson if (genpd->power_off) { 3451f8728b7SUlf Hansson int ret; 3461f8728b7SUlf Hansson 3471f8728b7SUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 3481f8728b7SUlf Hansson return -EBUSY; 3491f8728b7SUlf Hansson 3501f8728b7SUlf Hansson /* 3511f8728b7SUlf Hansson * If sd_count > 0 at this point, one of the subdomains hasn't 3521f8728b7SUlf Hansson * managed to call genpd_power_on() for the master yet after 3531f8728b7SUlf Hansson * incrementing it. In that case genpd_power_on() will wait 3541f8728b7SUlf Hansson * for us to drop the lock, so we can call .power_off() and let 3551f8728b7SUlf Hansson * the genpd_power_on() restore power for us (this shouldn't 3561f8728b7SUlf Hansson * happen very often). 3571f8728b7SUlf Hansson */ 3581f8728b7SUlf Hansson ret = _genpd_power_off(genpd, true); 3591f8728b7SUlf Hansson if (ret) 3601f8728b7SUlf Hansson return ret; 3611f8728b7SUlf Hansson } 3621f8728b7SUlf Hansson 3631f8728b7SUlf Hansson genpd->status = GPD_STATE_POWER_OFF; 3641f8728b7SUlf Hansson 3651f8728b7SUlf Hansson list_for_each_entry(link, &genpd->slave_links, slave_node) { 3661f8728b7SUlf Hansson genpd_sd_counter_dec(link->master); 3672da83545SUlf Hansson genpd_lock_nested(link->master, depth + 1); 3682da83545SUlf Hansson genpd_power_off(link->master, false, depth + 1); 3692da83545SUlf Hansson genpd_unlock(link->master); 3701f8728b7SUlf Hansson } 3711f8728b7SUlf Hansson 3721f8728b7SUlf Hansson return 0; 3731f8728b7SUlf Hansson } 3741f8728b7SUlf Hansson 3751f8728b7SUlf Hansson /** 37686e12eacSUlf Hansson * genpd_power_on - Restore power to a given PM domain and its masters. 3775248051bSRafael J. Wysocki * @genpd: PM domain to power up. 3780106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 3795248051bSRafael J. Wysocki * 3805063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 3815248051bSRafael J. Wysocki * resume a device belonging to it. 3825248051bSRafael J. Wysocki */ 38386e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) 3845248051bSRafael J. Wysocki { 3855063ce15SRafael J. Wysocki struct gpd_link *link; 3865248051bSRafael J. Wysocki int ret = 0; 3875248051bSRafael J. Wysocki 38841e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 3893f241775SRafael J. Wysocki return 0; 3905248051bSRafael J. Wysocki 3915063ce15SRafael J. Wysocki /* 3925063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 3935063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 3945063ce15SRafael J. Wysocki * with it. 3955063ce15SRafael J. Wysocki */ 3965063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 3970106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 3985248051bSRafael J. Wysocki 3990106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 4000106ef51SMarek Szyprowski 40135241d12SLina Iyer genpd_lock_nested(master, depth + 1); 40286e12eacSUlf Hansson ret = genpd_power_on(master, depth + 1); 40335241d12SLina Iyer genpd_unlock(master); 4040106ef51SMarek Szyprowski 4055063ce15SRafael J. Wysocki if (ret) { 4060106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 4079e08cf42SRafael J. Wysocki goto err; 4085248051bSRafael J. Wysocki } 4095063ce15SRafael J. Wysocki } 4105248051bSRafael J. Wysocki 41186e12eacSUlf Hansson ret = _genpd_power_on(genpd, true); 4129e08cf42SRafael J. Wysocki if (ret) 4139e08cf42SRafael J. Wysocki goto err; 4140140d8bdSRafael J. Wysocki 415ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 4163f241775SRafael J. Wysocki return 0; 4179e08cf42SRafael J. Wysocki 4189e08cf42SRafael J. Wysocki err: 41929e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 42029e47e21SUlf Hansson &genpd->slave_links, 42129e47e21SUlf Hansson slave_node) { 4225063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 4232da83545SUlf Hansson genpd_lock_nested(link->master, depth + 1); 4242da83545SUlf Hansson genpd_power_off(link->master, false, depth + 1); 4252da83545SUlf Hansson genpd_unlock(link->master); 42629e47e21SUlf Hansson } 4279e08cf42SRafael J. Wysocki 4283f241775SRafael J. Wysocki return ret; 4293f241775SRafael J. Wysocki } 4303f241775SRafael J. Wysocki 4316ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 4326ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 4336ff7bb0dSRafael J. Wysocki { 4346ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 4356ff7bb0dSRafael J. Wysocki struct device *dev; 4366ff7bb0dSRafael J. Wysocki 4376ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 4386ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 4396ff7bb0dSRafael J. Wysocki 4406ff7bb0dSRafael J. Wysocki for (;;) { 4416ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 4426ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 4436ff7bb0dSRafael J. Wysocki 4446ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 4456ff7bb0dSRafael J. Wysocki 4466ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 4476ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 448b4883ca4SViresh Kumar if (pdd) { 4496ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 4506ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 4516ff7bb0dSRafael J. Wysocki } else { 4526ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 4536ff7bb0dSRafael J. Wysocki } 4546ff7bb0dSRafael J. Wysocki 4556ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 4566ff7bb0dSRafael J. Wysocki 4576ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 45835241d12SLina Iyer genpd_lock(genpd); 4596ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 46035241d12SLina Iyer genpd_unlock(genpd); 4616ff7bb0dSRafael J. Wysocki } 4626ff7bb0dSRafael J. Wysocki 4636ff7bb0dSRafael J. Wysocki dev = dev->parent; 4646ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 4656ff7bb0dSRafael J. Wysocki break; 4666ff7bb0dSRafael J. Wysocki } 4676ff7bb0dSRafael J. Wysocki 4686ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 4696ff7bb0dSRafael J. Wysocki } 4706ff7bb0dSRafael J. Wysocki 4715248051bSRafael J. Wysocki /** 472f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 473f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 474f721889fSRafael J. Wysocki */ 475f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 476f721889fSRafael J. Wysocki { 477f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 478f721889fSRafael J. Wysocki 479f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 480f721889fSRafael J. Wysocki 48135241d12SLina Iyer genpd_lock(genpd); 4822da83545SUlf Hansson genpd_power_off(genpd, false, 0); 48335241d12SLina Iyer genpd_unlock(genpd); 484f721889fSRafael J. Wysocki } 485f721889fSRafael J. Wysocki 486f721889fSRafael J. Wysocki /** 48754eeddbfSUlf Hansson * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 48854eeddbfSUlf Hansson * @dev: Device to handle. 48954eeddbfSUlf Hansson */ 49054eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev) 49154eeddbfSUlf Hansson { 49254eeddbfSUlf Hansson int (*cb)(struct device *__dev); 49354eeddbfSUlf Hansson 49454eeddbfSUlf Hansson if (dev->type && dev->type->pm) 49554eeddbfSUlf Hansson cb = dev->type->pm->runtime_suspend; 49654eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 49754eeddbfSUlf Hansson cb = dev->class->pm->runtime_suspend; 49854eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 49954eeddbfSUlf Hansson cb = dev->bus->pm->runtime_suspend; 50054eeddbfSUlf Hansson else 50154eeddbfSUlf Hansson cb = NULL; 50254eeddbfSUlf Hansson 50354eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 50454eeddbfSUlf Hansson cb = dev->driver->pm->runtime_suspend; 50554eeddbfSUlf Hansson 50654eeddbfSUlf Hansson return cb ? cb(dev) : 0; 50754eeddbfSUlf Hansson } 50854eeddbfSUlf Hansson 50954eeddbfSUlf Hansson /** 51054eeddbfSUlf Hansson * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 51154eeddbfSUlf Hansson * @dev: Device to handle. 51254eeddbfSUlf Hansson */ 51354eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev) 51454eeddbfSUlf Hansson { 51554eeddbfSUlf Hansson int (*cb)(struct device *__dev); 51654eeddbfSUlf Hansson 51754eeddbfSUlf Hansson if (dev->type && dev->type->pm) 51854eeddbfSUlf Hansson cb = dev->type->pm->runtime_resume; 51954eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 52054eeddbfSUlf Hansson cb = dev->class->pm->runtime_resume; 52154eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 52254eeddbfSUlf Hansson cb = dev->bus->pm->runtime_resume; 52354eeddbfSUlf Hansson else 52454eeddbfSUlf Hansson cb = NULL; 52554eeddbfSUlf Hansson 52654eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 52754eeddbfSUlf Hansson cb = dev->driver->pm->runtime_resume; 52854eeddbfSUlf Hansson 52954eeddbfSUlf Hansson return cb ? cb(dev) : 0; 53054eeddbfSUlf Hansson } 53154eeddbfSUlf Hansson 53254eeddbfSUlf Hansson /** 533795bd2e7SUlf Hansson * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 534f721889fSRafael J. Wysocki * @dev: Device to suspend. 535f721889fSRafael J. Wysocki * 536f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 537f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 538f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 539f721889fSRafael J. Wysocki */ 540795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev) 541f721889fSRafael J. Wysocki { 542f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 5439df3921eSUlf Hansson bool (*suspend_ok)(struct device *__dev); 5442b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 545ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 5462b1d88cdSUlf Hansson ktime_t time_start; 5472b1d88cdSUlf Hansson s64 elapsed_ns; 548d5e4cbfeSRafael J. Wysocki int ret; 549f721889fSRafael J. Wysocki 550f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 551f721889fSRafael J. Wysocki 5525248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5535248051bSRafael J. Wysocki if (IS_ERR(genpd)) 554f721889fSRafael J. Wysocki return -EINVAL; 555f721889fSRafael J. Wysocki 556ffe12855SUlf Hansson /* 557ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 558ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 559ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 560ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 561ffe12855SUlf Hansson */ 5629df3921eSUlf Hansson suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 5639df3921eSUlf Hansson if (runtime_pm && suspend_ok && !suspend_ok(dev)) 564b02c999aSRafael J. Wysocki return -EBUSY; 565b02c999aSRafael J. Wysocki 5662b1d88cdSUlf Hansson /* Measure suspend latency. */ 567d33d5a6cSLinus Torvalds time_start = 0; 568ffe12855SUlf Hansson if (runtime_pm) 5692b1d88cdSUlf Hansson time_start = ktime_get(); 5702b1d88cdSUlf Hansson 57154eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 572f721889fSRafael J. Wysocki if (ret) 57317b75ecaSRafael J. Wysocki return ret; 57417b75ecaSRafael J. Wysocki 5752b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 576ba2bbfbfSUlf Hansson if (ret) { 57754eeddbfSUlf Hansson __genpd_runtime_resume(dev); 578ba2bbfbfSUlf Hansson return ret; 579ba2bbfbfSUlf Hansson } 580ba2bbfbfSUlf Hansson 5812b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 582ffe12855SUlf Hansson if (runtime_pm) { 5832b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5842b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 5852b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 5862b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 5872b1d88cdSUlf Hansson elapsed_ns); 5882b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5892b1d88cdSUlf Hansson td->constraint_changed = true; 5902b1d88cdSUlf Hansson } 591ffe12855SUlf Hansson } 5922b1d88cdSUlf Hansson 5930aa2a221SRafael J. Wysocki /* 594d716f479SLina Iyer * If power.irq_safe is set, this routine may be run with 595d716f479SLina Iyer * IRQs disabled, so suspend only if the PM domain also is irq_safe. 5960aa2a221SRafael J. Wysocki */ 597d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) 5980aa2a221SRafael J. Wysocki return 0; 5990aa2a221SRafael J. Wysocki 60035241d12SLina Iyer genpd_lock(genpd); 6012da83545SUlf Hansson genpd_power_off(genpd, true, 0); 60235241d12SLina Iyer genpd_unlock(genpd); 603f721889fSRafael J. Wysocki 604f721889fSRafael J. Wysocki return 0; 605f721889fSRafael J. Wysocki } 606f721889fSRafael J. Wysocki 607f721889fSRafael J. Wysocki /** 608795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 609f721889fSRafael J. Wysocki * @dev: Device to resume. 610f721889fSRafael J. Wysocki * 611f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 612f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 613f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 614f721889fSRafael J. Wysocki */ 615795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 616f721889fSRafael J. Wysocki { 617f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 6182b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 619ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 6202b1d88cdSUlf Hansson ktime_t time_start; 6212b1d88cdSUlf Hansson s64 elapsed_ns; 622f721889fSRafael J. Wysocki int ret; 623ba2bbfbfSUlf Hansson bool timed = true; 624f721889fSRafael J. Wysocki 625f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 626f721889fSRafael J. Wysocki 6275248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6285248051bSRafael J. Wysocki if (IS_ERR(genpd)) 629f721889fSRafael J. Wysocki return -EINVAL; 630f721889fSRafael J. Wysocki 631d716f479SLina Iyer /* 632d716f479SLina Iyer * As we don't power off a non IRQ safe domain, which holds 633d716f479SLina Iyer * an IRQ safe device, we don't need to restore power to it. 634d716f479SLina Iyer */ 635d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) { 636ba2bbfbfSUlf Hansson timed = false; 637ba2bbfbfSUlf Hansson goto out; 638ba2bbfbfSUlf Hansson } 6390aa2a221SRafael J. Wysocki 64035241d12SLina Iyer genpd_lock(genpd); 64186e12eacSUlf Hansson ret = genpd_power_on(genpd, 0); 64235241d12SLina Iyer genpd_unlock(genpd); 643ba2bbfbfSUlf Hansson 644ba2bbfbfSUlf Hansson if (ret) 6453f241775SRafael J. Wysocki return ret; 646c6d22b37SRafael J. Wysocki 647ba2bbfbfSUlf Hansson out: 6482b1d88cdSUlf Hansson /* Measure resume latency. */ 649ab51e6baSAugusto Mecking Caringi time_start = 0; 650ffe12855SUlf Hansson if (timed && runtime_pm) 6512b1d88cdSUlf Hansson time_start = ktime_get(); 6522b1d88cdSUlf Hansson 653076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 654076395caSLaurent Pinchart if (ret) 655076395caSLaurent Pinchart goto err_poweroff; 656076395caSLaurent Pinchart 65754eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 658076395caSLaurent Pinchart if (ret) 659076395caSLaurent Pinchart goto err_stop; 6602b1d88cdSUlf Hansson 6612b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 662ffe12855SUlf Hansson if (timed && runtime_pm) { 6632b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 6642b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 6652b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 6662b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 6672b1d88cdSUlf Hansson elapsed_ns); 6682b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 6692b1d88cdSUlf Hansson td->constraint_changed = true; 6702b1d88cdSUlf Hansson } 6712b1d88cdSUlf Hansson } 67217b75ecaSRafael J. Wysocki 673f721889fSRafael J. Wysocki return 0; 674076395caSLaurent Pinchart 675076395caSLaurent Pinchart err_stop: 676076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 677076395caSLaurent Pinchart err_poweroff: 678d716f479SLina Iyer if (!pm_runtime_is_irq_safe(dev) || 679d716f479SLina Iyer (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { 68035241d12SLina Iyer genpd_lock(genpd); 6812da83545SUlf Hansson genpd_power_off(genpd, true, 0); 68235241d12SLina Iyer genpd_unlock(genpd); 683076395caSLaurent Pinchart } 684076395caSLaurent Pinchart 685076395caSLaurent Pinchart return ret; 686f721889fSRafael J. Wysocki } 687f721889fSRafael J. Wysocki 68839ac5ba5STushar Behera static bool pd_ignore_unused; 68939ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 69039ac5ba5STushar Behera { 69139ac5ba5STushar Behera pd_ignore_unused = true; 69239ac5ba5STushar Behera return 1; 69339ac5ba5STushar Behera } 69439ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 69539ac5ba5STushar Behera 69617f2ae7fSRafael J. Wysocki /** 69786e12eacSUlf Hansson * genpd_power_off_unused - Power off all PM domains with no devices in use. 69817f2ae7fSRafael J. Wysocki */ 69986e12eacSUlf Hansson static int __init genpd_power_off_unused(void) 70017f2ae7fSRafael J. Wysocki { 70117f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 70217f2ae7fSRafael J. Wysocki 70339ac5ba5STushar Behera if (pd_ignore_unused) { 70439ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 705bb4b72fcSUlf Hansson return 0; 70639ac5ba5STushar Behera } 70739ac5ba5STushar Behera 70817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 70917f2ae7fSRafael J. Wysocki 71017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 71117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 71217f2ae7fSRafael J. Wysocki 71317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 71417f2ae7fSRafael J. Wysocki 7152fe71dcdSUlf Hansson return 0; 7162fe71dcdSUlf Hansson } 71786e12eacSUlf Hansson late_initcall(genpd_power_off_unused); 7182fe71dcdSUlf Hansson 7190159ec67SJon Hunter #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) 720596ba34bSRafael J. Wysocki 72177f827deSRafael J. Wysocki /** 72277f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 72377f827deSRafael J. Wysocki * @genpd: PM domain to check. 72477f827deSRafael J. Wysocki */ 725895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 72677f827deSRafael J. Wysocki { 727895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 72877f827deSRafael J. Wysocki 72977f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 73077f827deSRafael J. Wysocki return false; 73177f827deSRafael J. Wysocki 73277f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 73377f827deSRafael J. Wysocki if (gpd == genpd) 73477f827deSRafael J. Wysocki return true; 73577f827deSRafael J. Wysocki 73677f827deSRafael J. Wysocki return false; 73777f827deSRafael J. Wysocki } 73877f827deSRafael J. Wysocki 7390159ec67SJon Hunter #endif 7400159ec67SJon Hunter 7410159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 7420159ec67SJon Hunter 743d8600c8bSKrzysztof Kozlowski static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd, 744d5e4cbfeSRafael J. Wysocki struct device *dev) 745d5e4cbfeSRafael J. Wysocki { 746d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 747d5e4cbfeSRafael J. Wysocki } 748d5e4cbfeSRafael J. Wysocki 749596ba34bSRafael J. Wysocki /** 75086e12eacSUlf Hansson * genpd_sync_power_off - Synchronously power off a PM domain and its masters. 751596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 7520883ac03SUlf Hansson * @use_lock: use the lock. 7530883ac03SUlf Hansson * @depth: nesting count for lockdep. 754596ba34bSRafael J. Wysocki * 755596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7565063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 757596ba34bSRafael J. Wysocki * 75877f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 7590883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 7600883ac03SUlf Hansson * these cases the lock must be held. 761596ba34bSRafael J. Wysocki */ 7620883ac03SUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, 7630883ac03SUlf Hansson unsigned int depth) 764596ba34bSRafael J. Wysocki { 7655063ce15SRafael J. Wysocki struct gpd_link *link; 766596ba34bSRafael J. Wysocki 767ffaa42e8SUlf Hansson if (!genpd_status_on(genpd) || genpd_is_always_on(genpd)) 768596ba34bSRafael J. Wysocki return; 769596ba34bSRafael J. Wysocki 770c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 771c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 772596ba34bSRafael J. Wysocki return; 773596ba34bSRafael J. Wysocki 774fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 775fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 7761c14967cSUlf Hansson if (_genpd_power_off(genpd, false)) 7771c14967cSUlf Hansson return; 778596ba34bSRafael J. Wysocki 77917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7805063ce15SRafael J. Wysocki 7815063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7825063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 7830883ac03SUlf Hansson 7840883ac03SUlf Hansson if (use_lock) 7850883ac03SUlf Hansson genpd_lock_nested(link->master, depth + 1); 7860883ac03SUlf Hansson 7870883ac03SUlf Hansson genpd_sync_power_off(link->master, use_lock, depth + 1); 7880883ac03SUlf Hansson 7890883ac03SUlf Hansson if (use_lock) 7900883ac03SUlf Hansson genpd_unlock(link->master); 791596ba34bSRafael J. Wysocki } 792596ba34bSRafael J. Wysocki } 793596ba34bSRafael J. Wysocki 794596ba34bSRafael J. Wysocki /** 79586e12eacSUlf Hansson * genpd_sync_power_on - Synchronously power on a PM domain and its masters. 796802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 7970883ac03SUlf Hansson * @use_lock: use the lock. 7980883ac03SUlf Hansson * @depth: nesting count for lockdep. 799802d8b49SRafael J. Wysocki * 80077f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 8010883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 8020883ac03SUlf Hansson * these cases the lock must be held. 803802d8b49SRafael J. Wysocki */ 8040883ac03SUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, 8050883ac03SUlf Hansson unsigned int depth) 806802d8b49SRafael J. Wysocki { 807802d8b49SRafael J. Wysocki struct gpd_link *link; 808802d8b49SRafael J. Wysocki 80941e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 810802d8b49SRafael J. Wysocki return; 811802d8b49SRafael J. Wysocki 812802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 813802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 8140883ac03SUlf Hansson 8150883ac03SUlf Hansson if (use_lock) 8160883ac03SUlf Hansson genpd_lock_nested(link->master, depth + 1); 8170883ac03SUlf Hansson 8180883ac03SUlf Hansson genpd_sync_power_on(link->master, use_lock, depth + 1); 8190883ac03SUlf Hansson 8200883ac03SUlf Hansson if (use_lock) 8210883ac03SUlf Hansson genpd_unlock(link->master); 822802d8b49SRafael J. Wysocki } 823802d8b49SRafael J. Wysocki 82486e12eacSUlf Hansson _genpd_power_on(genpd, false); 825802d8b49SRafael J. Wysocki 826802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 827802d8b49SRafael J. Wysocki } 828802d8b49SRafael J. Wysocki 829802d8b49SRafael J. Wysocki /** 8304ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 8314ecd6e65SRafael J. Wysocki * @dev: Device to check. 8324ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 8334ecd6e65SRafael J. Wysocki * 8344ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 8354ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 8364ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 8374ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 8384ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 8394ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 8404ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8414ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8424ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8434ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8444ecd6e65SRafael J. Wysocki */ 845d8600c8bSKrzysztof Kozlowski static bool resume_needed(struct device *dev, 846d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd) 8474ecd6e65SRafael J. Wysocki { 8484ecd6e65SRafael J. Wysocki bool active_wakeup; 8494ecd6e65SRafael J. Wysocki 8504ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8514ecd6e65SRafael J. Wysocki return false; 8524ecd6e65SRafael J. Wysocki 853d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8544ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8554ecd6e65SRafael J. Wysocki } 8564ecd6e65SRafael J. Wysocki 8574ecd6e65SRafael J. Wysocki /** 858596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 859596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 860596ba34bSRafael J. Wysocki * 861596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 862596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 863596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 864596ba34bSRafael J. Wysocki * consisting of I/O devices. 865596ba34bSRafael J. Wysocki */ 866596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 867596ba34bSRafael J. Wysocki { 868596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 869b6c10c84SRafael J. Wysocki int ret; 870596ba34bSRafael J. Wysocki 871596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 872596ba34bSRafael J. Wysocki 873596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 874596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 875596ba34bSRafael J. Wysocki return -EINVAL; 876596ba34bSRafael J. Wysocki 87717b75ecaSRafael J. Wysocki /* 87817b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 87917b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 88017b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 88117b75ecaSRafael J. Wysocki */ 8824ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8834ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8844ecd6e65SRafael J. Wysocki 88535241d12SLina Iyer genpd_lock(genpd); 886596ba34bSRafael J. Wysocki 88739dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 88865533bbfSRafael J. Wysocki genpd->suspended_count = 0; 88917b75ecaSRafael J. Wysocki 89035241d12SLina Iyer genpd_unlock(genpd); 891596ba34bSRafael J. Wysocki 892b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 893b6c10c84SRafael J. Wysocki if (ret) { 89435241d12SLina Iyer genpd_lock(genpd); 895b6c10c84SRafael J. Wysocki 89639dd0f23SUlf Hansson genpd->prepared_count--; 897b6c10c84SRafael J. Wysocki 89835241d12SLina Iyer genpd_unlock(genpd); 899b6c10c84SRafael J. Wysocki } 90017b75ecaSRafael J. Wysocki 901b6c10c84SRafael J. Wysocki return ret; 902596ba34bSRafael J. Wysocki } 903596ba34bSRafael J. Wysocki 904596ba34bSRafael J. Wysocki /** 90510da6542SMikko Perttunen * genpd_finish_suspend - Completion of suspend or hibernation of device in an 90610da6542SMikko Perttunen * I/O pm domain. 9070496c8aeSRafael J. Wysocki * @dev: Device to suspend. 90810da6542SMikko Perttunen * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback. 9090496c8aeSRafael J. Wysocki * 9100496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 9110496c8aeSRafael J. Wysocki * been stopped. 9120496c8aeSRafael J. Wysocki */ 91310da6542SMikko Perttunen static int genpd_finish_suspend(struct device *dev, bool poweroff) 9140496c8aeSRafael J. Wysocki { 9150496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 916122a2237SUlf Hansson int ret; 917596ba34bSRafael J. Wysocki 9180496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9190496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9200496c8aeSRafael J. Wysocki return -EINVAL; 9210496c8aeSRafael J. Wysocki 92239dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 923d4f2d87aSRafael J. Wysocki return 0; 924d4f2d87aSRafael J. Wysocki 92510da6542SMikko Perttunen if (poweroff) 92610da6542SMikko Perttunen ret = pm_generic_poweroff_noirq(dev); 92710da6542SMikko Perttunen else 92810da6542SMikko Perttunen ret = pm_generic_suspend_noirq(dev); 92910da6542SMikko Perttunen if (ret) 93010da6542SMikko Perttunen return ret; 93110da6542SMikko Perttunen 932122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) { 933122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 934122a2237SUlf Hansson if (ret) 935122a2237SUlf Hansson return ret; 936122a2237SUlf Hansson } 937122a2237SUlf Hansson 9380883ac03SUlf Hansson genpd_lock(genpd); 939596ba34bSRafael J. Wysocki genpd->suspended_count++; 9400883ac03SUlf Hansson genpd_sync_power_off(genpd, true, 0); 9410883ac03SUlf Hansson genpd_unlock(genpd); 942596ba34bSRafael J. Wysocki 943596ba34bSRafael J. Wysocki return 0; 944596ba34bSRafael J. Wysocki } 945596ba34bSRafael J. Wysocki 946596ba34bSRafael J. Wysocki /** 94710da6542SMikko Perttunen * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 94810da6542SMikko Perttunen * @dev: Device to suspend. 94910da6542SMikko Perttunen * 95010da6542SMikko Perttunen * Stop the device and remove power from the domain if all devices in it have 95110da6542SMikko Perttunen * been stopped. 95210da6542SMikko Perttunen */ 95310da6542SMikko Perttunen static int pm_genpd_suspend_noirq(struct device *dev) 95410da6542SMikko Perttunen { 95510da6542SMikko Perttunen dev_dbg(dev, "%s()\n", __func__); 95610da6542SMikko Perttunen 95710da6542SMikko Perttunen return genpd_finish_suspend(dev, false); 95810da6542SMikko Perttunen } 95910da6542SMikko Perttunen 96010da6542SMikko Perttunen /** 9610496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 962596ba34bSRafael J. Wysocki * @dev: Device to resume. 963596ba34bSRafael J. Wysocki * 9640496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 965596ba34bSRafael J. Wysocki */ 966596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 967596ba34bSRafael J. Wysocki { 968596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 969122a2237SUlf Hansson int ret = 0; 970596ba34bSRafael J. Wysocki 971596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 972596ba34bSRafael J. Wysocki 973596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 974596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 975596ba34bSRafael J. Wysocki return -EINVAL; 976596ba34bSRafael J. Wysocki 97739dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 978596ba34bSRafael J. Wysocki return 0; 979596ba34bSRafael J. Wysocki 9800883ac03SUlf Hansson genpd_lock(genpd); 9810883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 982596ba34bSRafael J. Wysocki genpd->suspended_count--; 9830883ac03SUlf Hansson genpd_unlock(genpd); 984596ba34bSRafael J. Wysocki 985122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 986122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 987122a2237SUlf Hansson 98810da6542SMikko Perttunen ret = pm_generic_resume_noirq(dev); 98910da6542SMikko Perttunen if (ret) 99010da6542SMikko Perttunen return ret; 99110da6542SMikko Perttunen 992122a2237SUlf Hansson return ret; 993596ba34bSRafael J. Wysocki } 994596ba34bSRafael J. Wysocki 995596ba34bSRafael J. Wysocki /** 9960496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 997596ba34bSRafael J. Wysocki * @dev: Device to freeze. 998596ba34bSRafael J. Wysocki * 999596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1000596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1001596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1002596ba34bSRafael J. Wysocki * devices. 1003596ba34bSRafael J. Wysocki */ 1004596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1005596ba34bSRafael J. Wysocki { 1006d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd; 1007122a2237SUlf Hansson int ret = 0; 1008596ba34bSRafael J. Wysocki 1009596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1010596ba34bSRafael J. Wysocki 1011596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1012596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1013596ba34bSRafael J. Wysocki return -EINVAL; 1014596ba34bSRafael J. Wysocki 101510da6542SMikko Perttunen ret = pm_generic_freeze_noirq(dev); 101610da6542SMikko Perttunen if (ret) 101710da6542SMikko Perttunen return ret; 101810da6542SMikko Perttunen 1019122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 1020122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 1021122a2237SUlf Hansson 1022122a2237SUlf Hansson return ret; 1023596ba34bSRafael J. Wysocki } 1024596ba34bSRafael J. Wysocki 1025596ba34bSRafael J. Wysocki /** 10260496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1027596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1028596ba34bSRafael J. Wysocki * 10290496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 10300496c8aeSRafael J. Wysocki * before the system transition. 1031596ba34bSRafael J. Wysocki */ 1032596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1033596ba34bSRafael J. Wysocki { 1034d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd; 1035122a2237SUlf Hansson int ret = 0; 1036596ba34bSRafael J. Wysocki 1037596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1038596ba34bSRafael J. Wysocki 1039596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1040596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1041596ba34bSRafael J. Wysocki return -EINVAL; 1042596ba34bSRafael J. Wysocki 104310da6542SMikko Perttunen if (genpd->dev_ops.stop && genpd->dev_ops.start) { 1044122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 104510da6542SMikko Perttunen if (ret) 1046122a2237SUlf Hansson return ret; 10470496c8aeSRafael J. Wysocki } 1048596ba34bSRafael J. Wysocki 104910da6542SMikko Perttunen return pm_generic_thaw_noirq(dev); 105010da6542SMikko Perttunen } 105110da6542SMikko Perttunen 105210da6542SMikko Perttunen /** 105310da6542SMikko Perttunen * pm_genpd_poweroff_noirq - Completion of hibernation of device in an 105410da6542SMikko Perttunen * I/O PM domain. 105510da6542SMikko Perttunen * @dev: Device to poweroff. 105610da6542SMikko Perttunen * 105710da6542SMikko Perttunen * Stop the device and remove power from the domain if all devices in it have 105810da6542SMikko Perttunen * been stopped. 105910da6542SMikko Perttunen */ 106010da6542SMikko Perttunen static int pm_genpd_poweroff_noirq(struct device *dev) 106110da6542SMikko Perttunen { 106210da6542SMikko Perttunen dev_dbg(dev, "%s()\n", __func__); 106310da6542SMikko Perttunen 106410da6542SMikko Perttunen return genpd_finish_suspend(dev, true); 106510da6542SMikko Perttunen } 106610da6542SMikko Perttunen 10670496c8aeSRafael J. Wysocki /** 10680496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1069596ba34bSRafael J. Wysocki * @dev: Device to resume. 1070596ba34bSRafael J. Wysocki * 10710496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 10720496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1073596ba34bSRafael J. Wysocki */ 1074596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1075596ba34bSRafael J. Wysocki { 1076596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1077122a2237SUlf Hansson int ret = 0; 1078596ba34bSRafael J. Wysocki 1079596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1080596ba34bSRafael J. Wysocki 1081596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1082596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1083596ba34bSRafael J. Wysocki return -EINVAL; 1084596ba34bSRafael J. Wysocki 1085596ba34bSRafael J. Wysocki /* 108665533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 108765533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 108865533bbfSRafael J. Wysocki */ 10890883ac03SUlf Hansson genpd_lock(genpd); 109039dd0f23SUlf Hansson if (genpd->suspended_count++ == 0) 109165533bbfSRafael J. Wysocki /* 109265533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 109386e12eacSUlf Hansson * so make it appear as powered off to genpd_sync_power_on(), 1094802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1095596ba34bSRafael J. Wysocki */ 109617b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 109718dd2eceSRafael J. Wysocki 10980883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 10990883ac03SUlf Hansson genpd_unlock(genpd); 1100596ba34bSRafael J. Wysocki 110110da6542SMikko Perttunen if (genpd->dev_ops.stop && genpd->dev_ops.start) { 1102122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 110310da6542SMikko Perttunen if (ret) 1104122a2237SUlf Hansson return ret; 1105596ba34bSRafael J. Wysocki } 1106596ba34bSRafael J. Wysocki 110710da6542SMikko Perttunen return pm_generic_restore_noirq(dev); 110810da6542SMikko Perttunen } 110910da6542SMikko Perttunen 1110596ba34bSRafael J. Wysocki /** 1111596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1112596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1113596ba34bSRafael J. Wysocki * 1114596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1115596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1116596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1117596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1118596ba34bSRafael J. Wysocki */ 1119596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1120596ba34bSRafael J. Wysocki { 1121596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1122596ba34bSRafael J. Wysocki 1123596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1124596ba34bSRafael J. Wysocki 1125596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1126596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1127596ba34bSRafael J. Wysocki return; 1128596ba34bSRafael J. Wysocki 11294d23a5e8SUlf Hansson pm_generic_complete(dev); 11304d23a5e8SUlf Hansson 113135241d12SLina Iyer genpd_lock(genpd); 1132596ba34bSRafael J. Wysocki 113339dd0f23SUlf Hansson genpd->prepared_count--; 11344d23a5e8SUlf Hansson if (!genpd->prepared_count) 11354d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 1136596ba34bSRafael J. Wysocki 113735241d12SLina Iyer genpd_unlock(genpd); 1138596ba34bSRafael J. Wysocki } 1139596ba34bSRafael J. Wysocki 114077f827deSRafael J. Wysocki /** 1141d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 114277f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 114377f827deSRafael J. Wysocki * 114477f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 114577f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 114677f827deSRafael J. Wysocki */ 1147d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 114877f827deSRafael J. Wysocki { 114977f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 115077f827deSRafael J. Wysocki 115123c6d2c7SRafael J. Wysocki genpd = dev_to_genpd(dev); 115223c6d2c7SRafael J. Wysocki if (!pm_genpd_present(genpd)) 115377f827deSRafael J. Wysocki return; 115477f827deSRafael J. Wysocki 115577f827deSRafael J. Wysocki if (suspend) { 115677f827deSRafael J. Wysocki genpd->suspended_count++; 11570883ac03SUlf Hansson genpd_sync_power_off(genpd, false, 0); 115877f827deSRafael J. Wysocki } else { 11590883ac03SUlf Hansson genpd_sync_power_on(genpd, false, 0); 116077f827deSRafael J. Wysocki genpd->suspended_count--; 116177f827deSRafael J. Wysocki } 116277f827deSRafael J. Wysocki } 1163d47e6464SUlf Hansson 1164d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1165d47e6464SUlf Hansson { 1166d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1167d47e6464SUlf Hansson } 1168d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1169d47e6464SUlf Hansson 1170d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1171d47e6464SUlf Hansson { 1172d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1173d47e6464SUlf Hansson } 1174d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 117577f827deSRafael J. Wysocki 1176d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1177596ba34bSRafael J. Wysocki 1178596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1179596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 1180596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1181596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 1182596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1183596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1184596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1185596ba34bSRafael J. Wysocki 1186596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1187596ba34bSRafael J. Wysocki 1188f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1189f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1190f104e1e5SUlf Hansson struct gpd_timing_data *td) 11911d5fcfecSRafael J. Wysocki { 11921d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11933e235685SUlf Hansson int ret; 11943e235685SUlf Hansson 11953e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11963e235685SUlf Hansson if (ret) 11973e235685SUlf Hansson return ERR_PTR(ret); 11981d5fcfecSRafael J. Wysocki 11991d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 12003e235685SUlf Hansson if (!gpd_data) { 12013e235685SUlf Hansson ret = -ENOMEM; 12023e235685SUlf Hansson goto err_put; 12033e235685SUlf Hansson } 12041d5fcfecSRafael J. Wysocki 1205f104e1e5SUlf Hansson if (td) 1206f104e1e5SUlf Hansson gpd_data->td = *td; 1207f104e1e5SUlf Hansson 1208f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1209f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1210f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1211f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1212f104e1e5SUlf Hansson 1213f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1214f104e1e5SUlf Hansson 1215f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1216f104e1e5SUlf Hansson ret = -EINVAL; 1217f104e1e5SUlf Hansson goto err_free; 1218f104e1e5SUlf Hansson } 1219f104e1e5SUlf Hansson 1220f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1221f104e1e5SUlf Hansson 1222f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1223f104e1e5SUlf Hansson 1224989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1225989561deSTomeu Vizoso 12261d5fcfecSRafael J. Wysocki return gpd_data; 12273e235685SUlf Hansson 1228f104e1e5SUlf Hansson err_free: 1229f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1230f104e1e5SUlf Hansson kfree(gpd_data); 12313e235685SUlf Hansson err_put: 12323e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12333e235685SUlf Hansson return ERR_PTR(ret); 12341d5fcfecSRafael J. Wysocki } 12351d5fcfecSRafael J. Wysocki 123649d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 12371d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 12381d5fcfecSRafael J. Wysocki { 1239989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1240989561deSTomeu Vizoso 1241f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1242f104e1e5SUlf Hansson 1243f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1244f104e1e5SUlf Hansson 1245f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1246f104e1e5SUlf Hansson 12471d5fcfecSRafael J. Wysocki kfree(gpd_data); 12483e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12491d5fcfecSRafael J. Wysocki } 12501d5fcfecSRafael J. Wysocki 125119efa5ffSJon Hunter static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1252b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1253f721889fSRafael J. Wysocki { 1254c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1255f721889fSRafael J. Wysocki int ret = 0; 1256f721889fSRafael J. Wysocki 1257f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1258f721889fSRafael J. Wysocki 1259f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1260f721889fSRafael J. Wysocki return -EINVAL; 1261f721889fSRafael J. Wysocki 1262f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 12633e235685SUlf Hansson if (IS_ERR(gpd_data)) 12643e235685SUlf Hansson return PTR_ERR(gpd_data); 12656ff7bb0dSRafael J. Wysocki 126635241d12SLina Iyer genpd_lock(genpd); 1267f721889fSRafael J. Wysocki 1268596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1269596ba34bSRafael J. Wysocki ret = -EAGAIN; 1270596ba34bSRafael J. Wysocki goto out; 1271596ba34bSRafael J. Wysocki } 1272596ba34bSRafael J. Wysocki 1273b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1274b472c2faSUlf Hansson if (ret) 1275b472c2faSUlf Hansson goto out; 1276d79b6fe1SGeert Uytterhoeven 127714b53064SUlf Hansson genpd->device_count++; 127814b53064SUlf Hansson genpd->max_off_time_changed = true; 127914b53064SUlf Hansson 12801d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 12816ff7bb0dSRafael J. Wysocki 1282f721889fSRafael J. Wysocki out: 128335241d12SLina Iyer genpd_unlock(genpd); 1284f721889fSRafael J. Wysocki 1285c0356db7SUlf Hansson if (ret) 1286c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1287c0356db7SUlf Hansson else 1288c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12891d5fcfecSRafael J. Wysocki 1290f721889fSRafael J. Wysocki return ret; 1291f721889fSRafael J. Wysocki } 129219efa5ffSJon Hunter 129319efa5ffSJon Hunter /** 129419efa5ffSJon Hunter * __pm_genpd_add_device - Add a device to an I/O PM domain. 129519efa5ffSJon Hunter * @genpd: PM domain to add the device to. 129619efa5ffSJon Hunter * @dev: Device to be added. 129719efa5ffSJon Hunter * @td: Set of PM QoS timing parameters to attach to the device. 129819efa5ffSJon Hunter */ 129919efa5ffSJon Hunter int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 130019efa5ffSJon Hunter struct gpd_timing_data *td) 130119efa5ffSJon Hunter { 130219efa5ffSJon Hunter int ret; 130319efa5ffSJon Hunter 130419efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 130519efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, td); 130619efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 130719efa5ffSJon Hunter 130819efa5ffSJon Hunter return ret; 130919efa5ffSJon Hunter } 131024c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1311f721889fSRafael J. Wysocki 131285168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1313f721889fSRafael J. Wysocki struct device *dev) 1314f721889fSRafael J. Wysocki { 13156ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13164605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1317efa69025SRafael J. Wysocki int ret = 0; 1318f721889fSRafael J. Wysocki 1319f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1320f721889fSRafael J. Wysocki 1321c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1322c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1323c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1324c0356db7SUlf Hansson 132535241d12SLina Iyer genpd_lock(genpd); 1326f721889fSRafael J. Wysocki 1327596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1328596ba34bSRafael J. Wysocki ret = -EAGAIN; 1329596ba34bSRafael J. Wysocki goto out; 1330596ba34bSRafael J. Wysocki } 1331596ba34bSRafael J. Wysocki 13326ff7bb0dSRafael J. Wysocki genpd->device_count--; 13336ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 13346ff7bb0dSRafael J. Wysocki 1335d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1336c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1337d79b6fe1SGeert Uytterhoeven 1338efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 13396ff7bb0dSRafael J. Wysocki 134035241d12SLina Iyer genpd_unlock(genpd); 13416ff7bb0dSRafael J. Wysocki 134249d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 13431d5fcfecSRafael J. Wysocki 13446ff7bb0dSRafael J. Wysocki return 0; 1345f721889fSRafael J. Wysocki 1346596ba34bSRafael J. Wysocki out: 134735241d12SLina Iyer genpd_unlock(genpd); 1348c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1349f721889fSRafael J. Wysocki 1350f721889fSRafael J. Wysocki return ret; 1351f721889fSRafael J. Wysocki } 135285168d56SUlf Hansson 135385168d56SUlf Hansson /** 135485168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 135585168d56SUlf Hansson * @genpd: PM domain to remove the device from. 135685168d56SUlf Hansson * @dev: Device to be removed. 135785168d56SUlf Hansson */ 135885168d56SUlf Hansson int pm_genpd_remove_device(struct generic_pm_domain *genpd, 135985168d56SUlf Hansson struct device *dev) 136085168d56SUlf Hansson { 136185168d56SUlf Hansson if (!genpd || genpd != genpd_lookup_dev(dev)) 136285168d56SUlf Hansson return -EINVAL; 136385168d56SUlf Hansson 136485168d56SUlf Hansson return genpd_remove_device(genpd, dev); 136585168d56SUlf Hansson } 136624c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1367f721889fSRafael J. Wysocki 136819efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1369bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1370f721889fSRafael J. Wysocki { 13712547923dSLina Iyer struct gpd_link *link, *itr; 1372f721889fSRafael J. Wysocki int ret = 0; 1373f721889fSRafael J. Wysocki 1374fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1375fb7268beSRafael J. Wysocki || genpd == subdomain) 1376f721889fSRafael J. Wysocki return -EINVAL; 1377f721889fSRafael J. Wysocki 1378d716f479SLina Iyer /* 1379d716f479SLina Iyer * If the domain can be powered on/off in an IRQ safe 1380d716f479SLina Iyer * context, ensure that the subdomain can also be 1381d716f479SLina Iyer * powered on/off in that context. 1382d716f479SLina Iyer */ 1383d716f479SLina Iyer if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 138444cae7d5SDan Carpenter WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 1385d716f479SLina Iyer genpd->name, subdomain->name); 1386d716f479SLina Iyer return -EINVAL; 1387d716f479SLina Iyer } 1388d716f479SLina Iyer 13892547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13902547923dSLina Iyer if (!link) 13912547923dSLina Iyer return -ENOMEM; 13922547923dSLina Iyer 139335241d12SLina Iyer genpd_lock(subdomain); 139435241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1395f721889fSRafael J. Wysocki 139641e2c8e0SUlf Hansson if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) { 1397f721889fSRafael J. Wysocki ret = -EINVAL; 1398f721889fSRafael J. Wysocki goto out; 1399f721889fSRafael J. Wysocki } 1400f721889fSRafael J. Wysocki 14012547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 14022547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1403f721889fSRafael J. Wysocki ret = -EINVAL; 1404f721889fSRafael J. Wysocki goto out; 1405f721889fSRafael J. Wysocki } 1406f721889fSRafael J. Wysocki } 1407f721889fSRafael J. Wysocki 14085063ce15SRafael J. Wysocki link->master = genpd; 14095063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1410bc0403ffSRafael J. Wysocki link->slave = subdomain; 1411bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 141241e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1413c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1414f721889fSRafael J. Wysocki 1415f721889fSRafael J. Wysocki out: 141635241d12SLina Iyer genpd_unlock(genpd); 141735241d12SLina Iyer genpd_unlock(subdomain); 14182547923dSLina Iyer if (ret) 14192547923dSLina Iyer kfree(link); 1420f721889fSRafael J. Wysocki return ret; 1421f721889fSRafael J. Wysocki } 142219efa5ffSJon Hunter 142319efa5ffSJon Hunter /** 142419efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 142519efa5ffSJon Hunter * @genpd: Master PM domain to add the subdomain to. 142619efa5ffSJon Hunter * @subdomain: Subdomain to be added. 142719efa5ffSJon Hunter */ 142819efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 142919efa5ffSJon Hunter struct generic_pm_domain *subdomain) 143019efa5ffSJon Hunter { 143119efa5ffSJon Hunter int ret; 143219efa5ffSJon Hunter 143319efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 143419efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 143519efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 143619efa5ffSJon Hunter 143719efa5ffSJon Hunter return ret; 143819efa5ffSJon Hunter } 1439d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1440f721889fSRafael J. Wysocki 1441f721889fSRafael J. Wysocki /** 1442f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1443f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 14445063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1445f721889fSRafael J. Wysocki */ 1446f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 14475063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1448f721889fSRafael J. Wysocki { 1449c6e83cacSKrzysztof Kozlowski struct gpd_link *l, *link; 1450f721889fSRafael J. Wysocki int ret = -EINVAL; 1451f721889fSRafael J. Wysocki 14525063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1453f721889fSRafael J. Wysocki return -EINVAL; 1454f721889fSRafael J. Wysocki 145535241d12SLina Iyer genpd_lock(subdomain); 145635241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1457f721889fSRafael J. Wysocki 1458beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 145930e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 146030e7a65bSJon Hunter subdomain->name); 146130e7a65bSJon Hunter ret = -EBUSY; 146230e7a65bSJon Hunter goto out; 146330e7a65bSJon Hunter } 146430e7a65bSJon Hunter 1465c6e83cacSKrzysztof Kozlowski list_for_each_entry_safe(link, l, &genpd->master_links, master_node) { 14665063ce15SRafael J. Wysocki if (link->slave != subdomain) 1467f721889fSRafael J. Wysocki continue; 1468f721889fSRafael J. Wysocki 14695063ce15SRafael J. Wysocki list_del(&link->master_node); 14705063ce15SRafael J. Wysocki list_del(&link->slave_node); 14715063ce15SRafael J. Wysocki kfree(link); 147241e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1473f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1474f721889fSRafael J. Wysocki 1475f721889fSRafael J. Wysocki ret = 0; 1476f721889fSRafael J. Wysocki break; 1477f721889fSRafael J. Wysocki } 1478f721889fSRafael J. Wysocki 147930e7a65bSJon Hunter out: 148035241d12SLina Iyer genpd_unlock(genpd); 148135241d12SLina Iyer genpd_unlock(subdomain); 1482f721889fSRafael J. Wysocki 1483f721889fSRafael J. Wysocki return ret; 1484f721889fSRafael J. Wysocki } 1485d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1486f721889fSRafael J. Wysocki 148759d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd) 148859d65b73SLina Iyer { 148959d65b73SLina Iyer struct genpd_power_state *state; 149059d65b73SLina Iyer 149159d65b73SLina Iyer state = kzalloc(sizeof(*state), GFP_KERNEL); 149259d65b73SLina Iyer if (!state) 149359d65b73SLina Iyer return -ENOMEM; 149459d65b73SLina Iyer 149559d65b73SLina Iyer genpd->states = state; 149659d65b73SLina Iyer genpd->state_count = 1; 149759d65b73SLina Iyer genpd->free = state; 149859d65b73SLina Iyer 149959d65b73SLina Iyer return 0; 150059d65b73SLina Iyer } 150159d65b73SLina Iyer 1502d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd) 1503d716f479SLina Iyer { 1504d716f479SLina Iyer if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 1505d716f479SLina Iyer spin_lock_init(&genpd->slock); 1506d716f479SLina Iyer genpd->lock_ops = &genpd_spin_ops; 1507d716f479SLina Iyer } else { 1508d716f479SLina Iyer mutex_init(&genpd->mlock); 1509d716f479SLina Iyer genpd->lock_ops = &genpd_mtx_ops; 1510d716f479SLina Iyer } 1511d716f479SLina Iyer } 1512d716f479SLina Iyer 1513d23b9b00SRafael J. Wysocki /** 1514f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1515f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1516f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1517f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 15187eb231c3SUlf Hansson * 15197eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1520f721889fSRafael J. Wysocki */ 15217eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1522f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1523f721889fSRafael J. Wysocki { 152459d65b73SLina Iyer int ret; 152559d65b73SLina Iyer 1526f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 15277eb231c3SUlf Hansson return -EINVAL; 1528f721889fSRafael J. Wysocki 15295063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 15305063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1531f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1532d716f479SLina Iyer genpd_lock_init(genpd); 1533f721889fSRafael J. Wysocki genpd->gov = gov; 1534f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1535c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 153617b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1537596ba34bSRafael J. Wysocki genpd->device_count = 0; 1538221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 15396ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1540de0aa06dSJon Hunter genpd->provider = NULL; 1541de0aa06dSJon Hunter genpd->has_provider = false; 1542795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1543795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 1544596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1545596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1546596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1547596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1548596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 154910da6542SMikko Perttunen genpd->domain.ops.poweroff_noirq = pm_genpd_poweroff_noirq; 1550596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1551596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1552c11f6f5bSUlf Hansson 1553c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1554c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1555c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1556c11f6f5bSUlf Hansson } 1557c11f6f5bSUlf Hansson 1558ffaa42e8SUlf Hansson /* Always-on domains must be powered on at initialization. */ 1559ffaa42e8SUlf Hansson if (genpd_is_always_on(genpd) && !genpd_status_on(genpd)) 1560ffaa42e8SUlf Hansson return -EINVAL; 1561ffaa42e8SUlf Hansson 1562fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 156359d65b73SLina Iyer if (genpd->state_count == 0) { 156459d65b73SLina Iyer ret = genpd_set_default_power_state(genpd); 156559d65b73SLina Iyer if (ret) 156659d65b73SLina Iyer return ret; 156759d65b73SLina Iyer } 1568fc5cbf0cSAxel Haslam 15695125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 15705125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 15715125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 15727eb231c3SUlf Hansson 15737eb231c3SUlf Hansson return 0; 15745125bbf3SRafael J. Wysocki } 1575be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1576aa42240aSTomasz Figa 15773fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 15783fe57710SJon Hunter { 15793fe57710SJon Hunter struct gpd_link *l, *link; 15803fe57710SJon Hunter 15813fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 15823fe57710SJon Hunter return -EINVAL; 15833fe57710SJon Hunter 158435241d12SLina Iyer genpd_lock(genpd); 15853fe57710SJon Hunter 15863fe57710SJon Hunter if (genpd->has_provider) { 158735241d12SLina Iyer genpd_unlock(genpd); 15883fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 15893fe57710SJon Hunter return -EBUSY; 15903fe57710SJon Hunter } 15913fe57710SJon Hunter 15923fe57710SJon Hunter if (!list_empty(&genpd->master_links) || genpd->device_count) { 159335241d12SLina Iyer genpd_unlock(genpd); 15943fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 15953fe57710SJon Hunter return -EBUSY; 15963fe57710SJon Hunter } 15973fe57710SJon Hunter 15983fe57710SJon Hunter list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { 15993fe57710SJon Hunter list_del(&link->master_node); 16003fe57710SJon Hunter list_del(&link->slave_node); 16013fe57710SJon Hunter kfree(link); 16023fe57710SJon Hunter } 16033fe57710SJon Hunter 16043fe57710SJon Hunter list_del(&genpd->gpd_list_node); 160535241d12SLina Iyer genpd_unlock(genpd); 16063fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 160759d65b73SLina Iyer kfree(genpd->free); 16083fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 16093fe57710SJon Hunter 16103fe57710SJon Hunter return 0; 16113fe57710SJon Hunter } 16123fe57710SJon Hunter 16133fe57710SJon Hunter /** 16143fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 16153fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 16163fe57710SJon Hunter * 16173fe57710SJon Hunter * To remove the PM domain, this function: 16183fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 16193fe57710SJon Hunter * if it was added. 16203fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 16213fe57710SJon Hunter * 16223fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 16233fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 16243fe57710SJon Hunter * devices associated with it. 16253fe57710SJon Hunter */ 16263fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 16273fe57710SJon Hunter { 16283fe57710SJon Hunter int ret; 16293fe57710SJon Hunter 16303fe57710SJon Hunter mutex_lock(&gpd_list_lock); 16313fe57710SJon Hunter ret = genpd_remove(genpd); 16323fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 16333fe57710SJon Hunter 16343fe57710SJon Hunter return ret; 16353fe57710SJon Hunter } 16363fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 16373fe57710SJon Hunter 1638aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1639892ebdccSJon Hunter 1640892ebdccSJon Hunter typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, 1641892ebdccSJon Hunter void *data); 1642892ebdccSJon Hunter 1643aa42240aSTomasz Figa /* 1644aa42240aSTomasz Figa * Device Tree based PM domain providers. 1645aa42240aSTomasz Figa * 1646aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1647aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1648aa42240aSTomasz Figa * 1649aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1650aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1651aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1652aa42240aSTomasz Figa * 1653aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1654892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1655892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 1656aa42240aSTomasz Figa * index. 1657aa42240aSTomasz Figa */ 1658aa42240aSTomasz Figa 1659aa42240aSTomasz Figa /** 1660aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1661aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1662aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1663aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1664aa42240aSTomasz Figa * into a PM domain. 1665aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1666aa42240aSTomasz Figa */ 1667aa42240aSTomasz Figa struct of_genpd_provider { 1668aa42240aSTomasz Figa struct list_head link; 1669aa42240aSTomasz Figa struct device_node *node; 1670aa42240aSTomasz Figa genpd_xlate_t xlate; 1671aa42240aSTomasz Figa void *data; 1672aa42240aSTomasz Figa }; 1673aa42240aSTomasz Figa 1674aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1675aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1676aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1677aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1678aa42240aSTomasz Figa 1679aa42240aSTomasz Figa /** 1680892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 1681aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1682aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1683aa42240aSTomasz Figa * 1684aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1685aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1686aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1687aa42240aSTomasz Figa */ 1688892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 1689aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1690aa42240aSTomasz Figa void *data) 1691aa42240aSTomasz Figa { 1692aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1693aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1694aa42240aSTomasz Figa return data; 1695aa42240aSTomasz Figa } 1696aa42240aSTomasz Figa 1697aa42240aSTomasz Figa /** 1698892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 1699aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1700aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1701aa42240aSTomasz Figa * 1702aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1703aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1704aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1705aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1706aa42240aSTomasz Figa */ 1707892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 1708aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1709aa42240aSTomasz Figa void *data) 1710aa42240aSTomasz Figa { 1711aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1712aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1713aa42240aSTomasz Figa 1714aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1715aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1716aa42240aSTomasz Figa 1717aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1718aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1719aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1720aa42240aSTomasz Figa } 1721aa42240aSTomasz Figa 1722aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1723aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1724aa42240aSTomasz Figa 1725aa42240aSTomasz Figa return genpd_data->domains[idx]; 1726aa42240aSTomasz Figa } 1727aa42240aSTomasz Figa 1728aa42240aSTomasz Figa /** 1729892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 1730aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1731aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1732aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1733aa42240aSTomasz Figa */ 1734892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1735aa42240aSTomasz Figa void *data) 1736aa42240aSTomasz Figa { 1737aa42240aSTomasz Figa struct of_genpd_provider *cp; 1738aa42240aSTomasz Figa 1739aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1740aa42240aSTomasz Figa if (!cp) 1741aa42240aSTomasz Figa return -ENOMEM; 1742aa42240aSTomasz Figa 1743aa42240aSTomasz Figa cp->node = of_node_get(np); 1744aa42240aSTomasz Figa cp->data = data; 1745aa42240aSTomasz Figa cp->xlate = xlate; 1746aa42240aSTomasz Figa 1747aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1748aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1749aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1750aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1751aa42240aSTomasz Figa 1752aa42240aSTomasz Figa return 0; 1753aa42240aSTomasz Figa } 1754892ebdccSJon Hunter 1755892ebdccSJon Hunter /** 1756892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 1757892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1758892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 1759892ebdccSJon Hunter */ 1760892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 1761892ebdccSJon Hunter struct generic_pm_domain *genpd) 1762892ebdccSJon Hunter { 17630159ec67SJon Hunter int ret = -EINVAL; 17640159ec67SJon Hunter 17650159ec67SJon Hunter if (!np || !genpd) 17660159ec67SJon Hunter return -EINVAL; 17670159ec67SJon Hunter 17680159ec67SJon Hunter mutex_lock(&gpd_list_lock); 17690159ec67SJon Hunter 17708ce95844SViresh Kumar if (pm_genpd_present(genpd)) { 17710159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 1772de0aa06dSJon Hunter if (!ret) { 1773de0aa06dSJon Hunter genpd->provider = &np->fwnode; 1774de0aa06dSJon Hunter genpd->has_provider = true; 1775de0aa06dSJon Hunter } 17768ce95844SViresh Kumar } 1777de0aa06dSJon Hunter 17780159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 17790159ec67SJon Hunter 17800159ec67SJon Hunter return ret; 1781892ebdccSJon Hunter } 1782892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 1783892ebdccSJon Hunter 1784892ebdccSJon Hunter /** 1785892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 1786892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1787892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 1788892ebdccSJon Hunter */ 1789892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 1790892ebdccSJon Hunter struct genpd_onecell_data *data) 1791892ebdccSJon Hunter { 17920159ec67SJon Hunter unsigned int i; 1793de0aa06dSJon Hunter int ret = -EINVAL; 17940159ec67SJon Hunter 17950159ec67SJon Hunter if (!np || !data) 17960159ec67SJon Hunter return -EINVAL; 17970159ec67SJon Hunter 17980159ec67SJon Hunter mutex_lock(&gpd_list_lock); 17990159ec67SJon Hunter 18000159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 1801609bed67STomeu Vizoso if (!data->domains[i]) 1802609bed67STomeu Vizoso continue; 1803de0aa06dSJon Hunter if (!pm_genpd_present(data->domains[i])) 1804de0aa06dSJon Hunter goto error; 1805de0aa06dSJon Hunter 1806de0aa06dSJon Hunter data->domains[i]->provider = &np->fwnode; 1807de0aa06dSJon Hunter data->domains[i]->has_provider = true; 18080159ec67SJon Hunter } 18090159ec67SJon Hunter 18100159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_onecell, data); 1811de0aa06dSJon Hunter if (ret < 0) 1812de0aa06dSJon Hunter goto error; 1813de0aa06dSJon Hunter 1814de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1815de0aa06dSJon Hunter 1816de0aa06dSJon Hunter return 0; 1817de0aa06dSJon Hunter 1818de0aa06dSJon Hunter error: 1819de0aa06dSJon Hunter while (i--) { 1820609bed67STomeu Vizoso if (!data->domains[i]) 1821609bed67STomeu Vizoso continue; 1822de0aa06dSJon Hunter data->domains[i]->provider = NULL; 1823de0aa06dSJon Hunter data->domains[i]->has_provider = false; 1824de0aa06dSJon Hunter } 18250159ec67SJon Hunter 18260159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 18270159ec67SJon Hunter 18280159ec67SJon Hunter return ret; 1829892ebdccSJon Hunter } 1830892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 1831aa42240aSTomasz Figa 1832aa42240aSTomasz Figa /** 1833aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1834aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1835aa42240aSTomasz Figa */ 1836aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1837aa42240aSTomasz Figa { 1838b556b15dSKrzysztof Kozlowski struct of_genpd_provider *cp, *tmp; 1839de0aa06dSJon Hunter struct generic_pm_domain *gpd; 1840aa42240aSTomasz Figa 1841de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 1842aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1843b556b15dSKrzysztof Kozlowski list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) { 1844aa42240aSTomasz Figa if (cp->node == np) { 1845de0aa06dSJon Hunter /* 1846de0aa06dSJon Hunter * For each PM domain associated with the 1847de0aa06dSJon Hunter * provider, set the 'has_provider' to false 1848de0aa06dSJon Hunter * so that the PM domain can be safely removed. 1849de0aa06dSJon Hunter */ 1850de0aa06dSJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) 1851de0aa06dSJon Hunter if (gpd->provider == &np->fwnode) 1852de0aa06dSJon Hunter gpd->has_provider = false; 1853de0aa06dSJon Hunter 1854aa42240aSTomasz Figa list_del(&cp->link); 1855aa42240aSTomasz Figa of_node_put(cp->node); 1856aa42240aSTomasz Figa kfree(cp); 1857aa42240aSTomasz Figa break; 1858aa42240aSTomasz Figa } 1859aa42240aSTomasz Figa } 1860aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1861de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1862aa42240aSTomasz Figa } 1863aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1864aa42240aSTomasz Figa 1865aa42240aSTomasz Figa /** 1866f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 1867aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1868aa42240aSTomasz Figa * 1869aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1870aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1871aa42240aSTomasz Figa * domain. 1872aa42240aSTomasz Figa * 1873aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1874aa42240aSTomasz Figa * on failure. 1875aa42240aSTomasz Figa */ 1876f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 1877aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1878aa42240aSTomasz Figa { 1879aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1880aa42240aSTomasz Figa struct of_genpd_provider *provider; 1881aa42240aSTomasz Figa 188241795a8aSJon Hunter if (!genpdspec) 188341795a8aSJon Hunter return ERR_PTR(-EINVAL); 188441795a8aSJon Hunter 1885aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1886aa42240aSTomasz Figa 1887aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1888aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1889aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1890aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1891aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1892aa42240aSTomasz Figa break; 1893aa42240aSTomasz Figa } 1894aa42240aSTomasz Figa 1895aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1896aa42240aSTomasz Figa 1897aa42240aSTomasz Figa return genpd; 1898aa42240aSTomasz Figa } 1899aa42240aSTomasz Figa 1900aa42240aSTomasz Figa /** 1901ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 1902ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 1903ec69572bSJon Hunter * @dev: Device to be added. 1904ec69572bSJon Hunter * 1905ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 1906ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 1907ec69572bSJon Hunter */ 1908ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 1909ec69572bSJon Hunter { 1910ec69572bSJon Hunter struct generic_pm_domain *genpd; 191119efa5ffSJon Hunter int ret; 191219efa5ffSJon Hunter 191319efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1914ec69572bSJon Hunter 1915f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 191619efa5ffSJon Hunter if (IS_ERR(genpd)) { 191719efa5ffSJon Hunter ret = PTR_ERR(genpd); 191819efa5ffSJon Hunter goto out; 191919efa5ffSJon Hunter } 1920ec69572bSJon Hunter 192119efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, NULL); 192219efa5ffSJon Hunter 192319efa5ffSJon Hunter out: 192419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 192519efa5ffSJon Hunter 192619efa5ffSJon Hunter return ret; 1927ec69572bSJon Hunter } 1928ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 1929ec69572bSJon Hunter 1930ec69572bSJon Hunter /** 1931ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1932ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 1933ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 1934ec69572bSJon Hunter * 1935ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 1936ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 1937ec69572bSJon Hunter * negative error code on failure. 1938ec69572bSJon Hunter */ 1939ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 1940ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 1941ec69572bSJon Hunter { 1942ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 194319efa5ffSJon Hunter int ret; 194419efa5ffSJon Hunter 194519efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1946ec69572bSJon Hunter 1947f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 194819efa5ffSJon Hunter if (IS_ERR(parent)) { 194919efa5ffSJon Hunter ret = PTR_ERR(parent); 195019efa5ffSJon Hunter goto out; 195119efa5ffSJon Hunter } 1952ec69572bSJon Hunter 1953f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 195419efa5ffSJon Hunter if (IS_ERR(subdomain)) { 195519efa5ffSJon Hunter ret = PTR_ERR(subdomain); 195619efa5ffSJon Hunter goto out; 195719efa5ffSJon Hunter } 1958ec69572bSJon Hunter 195919efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 196019efa5ffSJon Hunter 196119efa5ffSJon Hunter out: 196219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 196319efa5ffSJon Hunter 196419efa5ffSJon Hunter return ret; 1965ec69572bSJon Hunter } 1966ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 1967ec69572bSJon Hunter 1968ec69572bSJon Hunter /** 196917926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 197017926551SJon Hunter * @provider: Pointer to device structure associated with provider 197117926551SJon Hunter * 197217926551SJon Hunter * Find the last PM domain that was added by a particular provider and 197317926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 197417926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 197517926551SJon Hunter * domain will only be removed, if the provider associated with domain 197617926551SJon Hunter * has been removed. 197717926551SJon Hunter * 197817926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 197917926551SJon Hunter * ERR_PTR() on failure. 198017926551SJon Hunter */ 198117926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 198217926551SJon Hunter { 1983a7e2d1bcSKrzysztof Kozlowski struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT); 198417926551SJon Hunter int ret; 198517926551SJon Hunter 198617926551SJon Hunter if (IS_ERR_OR_NULL(np)) 198717926551SJon Hunter return ERR_PTR(-EINVAL); 198817926551SJon Hunter 198917926551SJon Hunter mutex_lock(&gpd_list_lock); 1990a7e2d1bcSKrzysztof Kozlowski list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { 199117926551SJon Hunter if (gpd->provider == &np->fwnode) { 199217926551SJon Hunter ret = genpd_remove(gpd); 199317926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 199417926551SJon Hunter break; 199517926551SJon Hunter } 199617926551SJon Hunter } 199717926551SJon Hunter mutex_unlock(&gpd_list_lock); 199817926551SJon Hunter 199917926551SJon Hunter return genpd; 200017926551SJon Hunter } 200117926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 200217926551SJon Hunter 200317926551SJon Hunter /** 2004aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 20058bb6944eSJon Hunter * @dev: Device to detach. 2006aa42240aSTomasz Figa * @power_off: Currently not used 2007aa42240aSTomasz Figa * 2008aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 2009aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 2010aa42240aSTomasz Figa */ 2011aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 2012aa42240aSTomasz Figa { 2013446d999cSRussell King struct generic_pm_domain *pd; 201493af5e93SGeert Uytterhoeven unsigned int i; 2015aa42240aSTomasz Figa int ret = 0; 2016aa42240aSTomasz Figa 201785168d56SUlf Hansson pd = dev_to_genpd(dev); 201885168d56SUlf Hansson if (IS_ERR(pd)) 2019aa42240aSTomasz Figa return; 2020aa42240aSTomasz Figa 2021aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 2022aa42240aSTomasz Figa 202393af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 202485168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 2025aa42240aSTomasz Figa if (ret != -EAGAIN) 2026aa42240aSTomasz Figa break; 202793af5e93SGeert Uytterhoeven 202893af5e93SGeert Uytterhoeven mdelay(i); 2029aa42240aSTomasz Figa cond_resched(); 2030aa42240aSTomasz Figa } 2031aa42240aSTomasz Figa 2032aa42240aSTomasz Figa if (ret < 0) { 2033aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 2034aa42240aSTomasz Figa pd->name, ret); 2035aa42240aSTomasz Figa return; 2036aa42240aSTomasz Figa } 2037aa42240aSTomasz Figa 2038aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 2039aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 2040aa42240aSTomasz Figa } 2041aa42240aSTomasz Figa 2042632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 2043632f7ce3SRussell King { 2044632f7ce3SRussell King struct generic_pm_domain *pd; 2045632f7ce3SRussell King 2046632f7ce3SRussell King pd = dev_to_genpd(dev); 2047632f7ce3SRussell King if (IS_ERR(pd)) 2048632f7ce3SRussell King return; 2049632f7ce3SRussell King 2050632f7ce3SRussell King genpd_queue_power_off_work(pd); 2051632f7ce3SRussell King } 2052632f7ce3SRussell King 2053aa42240aSTomasz Figa /** 2054aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 2055aa42240aSTomasz Figa * @dev: Device to attach. 2056aa42240aSTomasz Figa * 2057aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 2058aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 2059aa42240aSTomasz Figa * 2060aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 2061aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 2062aa42240aSTomasz Figa * 2063311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 2064311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 2065311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 2066311fa6adSJon Hunter * probed and to re-try again later. 2067aa42240aSTomasz Figa */ 2068aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 2069aa42240aSTomasz Figa { 2070aa42240aSTomasz Figa struct of_phandle_args pd_args; 2071aa42240aSTomasz Figa struct generic_pm_domain *pd; 207293af5e93SGeert Uytterhoeven unsigned int i; 2073aa42240aSTomasz Figa int ret; 2074aa42240aSTomasz Figa 2075aa42240aSTomasz Figa if (!dev->of_node) 2076aa42240aSTomasz Figa return -ENODEV; 2077aa42240aSTomasz Figa 2078aa42240aSTomasz Figa if (dev->pm_domain) 2079aa42240aSTomasz Figa return -EEXIST; 2080aa42240aSTomasz Figa 2081aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 2082aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 2083aa42240aSTomasz Figa if (ret < 0) { 2084aa42240aSTomasz Figa if (ret != -ENOENT) 2085aa42240aSTomasz Figa return ret; 2086aa42240aSTomasz Figa 2087aa42240aSTomasz Figa /* 2088aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 2089aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 2090aa42240aSTomasz Figa */ 2091aa42240aSTomasz Figa pd_args.args_count = 0; 2092aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 2093aa42240aSTomasz Figa "samsung,power-domain", 0); 2094aa42240aSTomasz Figa if (!pd_args.np) 2095aa42240aSTomasz Figa return -ENOENT; 2096aa42240aSTomasz Figa } 2097aa42240aSTomasz Figa 209819efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2099f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 2100265e2cf6SEric Anholt of_node_put(pd_args.np); 2101aa42240aSTomasz Figa if (IS_ERR(pd)) { 210219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2103aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 2104aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 2105311fa6adSJon Hunter return -EPROBE_DEFER; 2106aa42240aSTomasz Figa } 2107aa42240aSTomasz Figa 2108aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 2109aa42240aSTomasz Figa 211093af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 211119efa5ffSJon Hunter ret = genpd_add_device(pd, dev, NULL); 2112aa42240aSTomasz Figa if (ret != -EAGAIN) 2113aa42240aSTomasz Figa break; 211493af5e93SGeert Uytterhoeven 211593af5e93SGeert Uytterhoeven mdelay(i); 2116aa42240aSTomasz Figa cond_resched(); 2117aa42240aSTomasz Figa } 211819efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2119aa42240aSTomasz Figa 2120aa42240aSTomasz Figa if (ret < 0) { 212134994692SGeert Uytterhoeven if (ret != -EPROBE_DEFER) 2122aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 2123aa42240aSTomasz Figa pd->name, ret); 2124311fa6adSJon Hunter goto out; 2125aa42240aSTomasz Figa } 2126aa42240aSTomasz Figa 2127aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 2128632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 2129aa42240aSTomasz Figa 213035241d12SLina Iyer genpd_lock(pd); 213186e12eacSUlf Hansson ret = genpd_power_on(pd, 0); 213235241d12SLina Iyer genpd_unlock(pd); 2133311fa6adSJon Hunter out: 2134311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 2135aa42240aSTomasz Figa } 2136aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 213730f60428SLina Iyer 213830f60428SLina Iyer static const struct of_device_id idle_state_match[] = { 2139598da548SLina Iyer { .compatible = "domain-idle-state", }, 214030f60428SLina Iyer { } 214130f60428SLina Iyer }; 214230f60428SLina Iyer 214330f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state, 214430f60428SLina Iyer struct device_node *state_node) 214530f60428SLina Iyer { 214630f60428SLina Iyer int err; 214730f60428SLina Iyer u32 residency; 214830f60428SLina Iyer u32 entry_latency, exit_latency; 214930f60428SLina Iyer 215030f60428SLina Iyer err = of_property_read_u32(state_node, "entry-latency-us", 215130f60428SLina Iyer &entry_latency); 215230f60428SLina Iyer if (err) { 215330f60428SLina Iyer pr_debug(" * %s missing entry-latency-us property\n", 215430f60428SLina Iyer state_node->full_name); 215530f60428SLina Iyer return -EINVAL; 215630f60428SLina Iyer } 215730f60428SLina Iyer 215830f60428SLina Iyer err = of_property_read_u32(state_node, "exit-latency-us", 215930f60428SLina Iyer &exit_latency); 216030f60428SLina Iyer if (err) { 216130f60428SLina Iyer pr_debug(" * %s missing exit-latency-us property\n", 216230f60428SLina Iyer state_node->full_name); 216330f60428SLina Iyer return -EINVAL; 216430f60428SLina Iyer } 216530f60428SLina Iyer 216630f60428SLina Iyer err = of_property_read_u32(state_node, "min-residency-us", &residency); 216730f60428SLina Iyer if (!err) 216830f60428SLina Iyer genpd_state->residency_ns = 1000 * residency; 216930f60428SLina Iyer 217030f60428SLina Iyer genpd_state->power_on_latency_ns = 1000 * exit_latency; 217130f60428SLina Iyer genpd_state->power_off_latency_ns = 1000 * entry_latency; 21720c9b694aSLina Iyer genpd_state->fwnode = &state_node->fwnode; 217330f60428SLina Iyer 217430f60428SLina Iyer return 0; 217530f60428SLina Iyer } 217630f60428SLina Iyer 217730f60428SLina Iyer /** 217830f60428SLina Iyer * of_genpd_parse_idle_states: Return array of idle states for the genpd. 217930f60428SLina Iyer * 218030f60428SLina Iyer * @dn: The genpd device node 218130f60428SLina Iyer * @states: The pointer to which the state array will be saved. 218230f60428SLina Iyer * @n: The count of elements in the array returned from this function. 218330f60428SLina Iyer * 218430f60428SLina Iyer * Returns the device states parsed from the OF node. The memory for the states 218530f60428SLina Iyer * is allocated by this function and is the responsibility of the caller to 218630f60428SLina Iyer * free the memory after use. 218730f60428SLina Iyer */ 218830f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn, 218930f60428SLina Iyer struct genpd_power_state **states, int *n) 219030f60428SLina Iyer { 219130f60428SLina Iyer struct genpd_power_state *st; 219230f60428SLina Iyer struct device_node *np; 219330f60428SLina Iyer int i = 0; 219430f60428SLina Iyer int err, ret; 219530f60428SLina Iyer int count; 219630f60428SLina Iyer struct of_phandle_iterator it; 2197b539cc82SLina Iyer const struct of_device_id *match_id; 219830f60428SLina Iyer 219930f60428SLina Iyer count = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 2200a1fee00dSColin Ian King if (count <= 0) 220130f60428SLina Iyer return -EINVAL; 220230f60428SLina Iyer 220330f60428SLina Iyer st = kcalloc(count, sizeof(*st), GFP_KERNEL); 220430f60428SLina Iyer if (!st) 220530f60428SLina Iyer return -ENOMEM; 220630f60428SLina Iyer 220730f60428SLina Iyer /* Loop over the phandles until all the requested entry is found */ 220830f60428SLina Iyer of_for_each_phandle(&it, err, dn, "domain-idle-states", NULL, 0) { 220930f60428SLina Iyer np = it.node; 2210b539cc82SLina Iyer match_id = of_match_node(idle_state_match, np); 2211b539cc82SLina Iyer if (!match_id) 2212b539cc82SLina Iyer continue; 221330f60428SLina Iyer ret = genpd_parse_state(&st[i++], np); 221430f60428SLina Iyer if (ret) { 221530f60428SLina Iyer pr_err 221630f60428SLina Iyer ("Parsing idle state node %s failed with err %d\n", 221730f60428SLina Iyer np->full_name, ret); 221830f60428SLina Iyer of_node_put(np); 221930f60428SLina Iyer kfree(st); 222030f60428SLina Iyer return ret; 222130f60428SLina Iyer } 222230f60428SLina Iyer } 222330f60428SLina Iyer 2224b539cc82SLina Iyer *n = i; 2225b539cc82SLina Iyer if (!i) 2226b539cc82SLina Iyer kfree(st); 2227b539cc82SLina Iyer else 222830f60428SLina Iyer *states = st; 222930f60428SLina Iyer 223030f60428SLina Iyer return 0; 223130f60428SLina Iyer } 223230f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 223330f60428SLina Iyer 2234d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 22352bd5306aSMaciej Matraszek 22362bd5306aSMaciej Matraszek 22372bd5306aSMaciej Matraszek /*** debugfs support ***/ 22382bd5306aSMaciej Matraszek 22398b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 22402bd5306aSMaciej Matraszek #include <linux/pm.h> 22412bd5306aSMaciej Matraszek #include <linux/device.h> 22422bd5306aSMaciej Matraszek #include <linux/debugfs.h> 22432bd5306aSMaciej Matraszek #include <linux/seq_file.h> 22442bd5306aSMaciej Matraszek #include <linux/init.h> 22452bd5306aSMaciej Matraszek #include <linux/kobject.h> 22462bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 22472bd5306aSMaciej Matraszek 22482bd5306aSMaciej Matraszek /* 22492bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 2250d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 22512bd5306aSMaciej Matraszek */ 22522bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 22532bd5306aSMaciej Matraszek { 22542bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 22552bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 22562bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 22572bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 22582bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 22592bd5306aSMaciej Matraszek }; 22602bd5306aSMaciej Matraszek const char *p = ""; 22612bd5306aSMaciej Matraszek 22622bd5306aSMaciej Matraszek if (dev->power.runtime_error) 22632bd5306aSMaciej Matraszek p = "error"; 22642bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 22652bd5306aSMaciej Matraszek p = "unsupported"; 22662bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 22672bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 22682bd5306aSMaciej Matraszek else 22692bd5306aSMaciej Matraszek WARN_ON(1); 22702bd5306aSMaciej Matraszek 22712bd5306aSMaciej Matraszek seq_puts(s, p); 22722bd5306aSMaciej Matraszek } 22732bd5306aSMaciej Matraszek 22742bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 227566a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 22762bd5306aSMaciej Matraszek { 22772bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 22782bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 22792bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 22802bd5306aSMaciej Matraszek }; 22812bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 22822bd5306aSMaciej Matraszek const char *kobj_path; 22832bd5306aSMaciej Matraszek struct gpd_link *link; 22846954d432SGeert Uytterhoeven char state[16]; 22852bd5306aSMaciej Matraszek int ret; 22862bd5306aSMaciej Matraszek 228735241d12SLina Iyer ret = genpd_lock_interruptible(genpd); 22882bd5306aSMaciej Matraszek if (ret) 22892bd5306aSMaciej Matraszek return -ERESTARTSYS; 22902bd5306aSMaciej Matraszek 229166a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 22922bd5306aSMaciej Matraszek goto exit; 229341e2c8e0SUlf Hansson if (!genpd_status_on(genpd)) 22940ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 22956954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 2296fc5cbf0cSAxel Haslam else 22976954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 22986954d432SGeert Uytterhoeven status_lookup[genpd->status]); 22996954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 23002bd5306aSMaciej Matraszek 23012bd5306aSMaciej Matraszek /* 23022bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 23032bd5306aSMaciej Matraszek * master and slave, so we are safe. 230466a5ca4bSKevin Hilman * Also genpd->name is immutable. 23052bd5306aSMaciej Matraszek */ 230666a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 23072bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 230866a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 23092bd5306aSMaciej Matraszek seq_puts(s, ", "); 23102bd5306aSMaciej Matraszek } 23112bd5306aSMaciej Matraszek 231266a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 2313d716f479SLina Iyer kobj_path = kobject_get_path(&pm_data->dev->kobj, 2314d716f479SLina Iyer genpd_is_irq_safe(genpd) ? 2315d716f479SLina Iyer GFP_ATOMIC : GFP_KERNEL); 23162bd5306aSMaciej Matraszek if (kobj_path == NULL) 23172bd5306aSMaciej Matraszek continue; 23182bd5306aSMaciej Matraszek 23192bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 23202bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 23212bd5306aSMaciej Matraszek kfree(kobj_path); 23222bd5306aSMaciej Matraszek } 23232bd5306aSMaciej Matraszek 23242bd5306aSMaciej Matraszek seq_puts(s, "\n"); 23252bd5306aSMaciej Matraszek exit: 232635241d12SLina Iyer genpd_unlock(genpd); 23272bd5306aSMaciej Matraszek 23282bd5306aSMaciej Matraszek return 0; 23292bd5306aSMaciej Matraszek } 23302bd5306aSMaciej Matraszek 23312bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 23322bd5306aSMaciej Matraszek { 233366a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 23342bd5306aSMaciej Matraszek int ret = 0; 23352bd5306aSMaciej Matraszek 23362bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 23372bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 23382bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 23392bd5306aSMaciej Matraszek 23402bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 23412bd5306aSMaciej Matraszek if (ret) 23422bd5306aSMaciej Matraszek return -ERESTARTSYS; 23432bd5306aSMaciej Matraszek 234466a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 234566a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 23462bd5306aSMaciej Matraszek if (ret) 23472bd5306aSMaciej Matraszek break; 23482bd5306aSMaciej Matraszek } 23492bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 23502bd5306aSMaciej Matraszek 23512bd5306aSMaciej Matraszek return ret; 23522bd5306aSMaciej Matraszek } 23532bd5306aSMaciej Matraszek 23542bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 23552bd5306aSMaciej Matraszek { 23562bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 23572bd5306aSMaciej Matraszek } 23582bd5306aSMaciej Matraszek 23592bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 23602bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 23612bd5306aSMaciej Matraszek .read = seq_read, 23622bd5306aSMaciej Matraszek .llseek = seq_lseek, 23632bd5306aSMaciej Matraszek .release = single_release, 23642bd5306aSMaciej Matraszek }; 23652bd5306aSMaciej Matraszek 23662bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 23672bd5306aSMaciej Matraszek { 23682bd5306aSMaciej Matraszek struct dentry *d; 23692bd5306aSMaciej Matraszek 23702bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 23712bd5306aSMaciej Matraszek 23722bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 23732bd5306aSMaciej Matraszek return -ENOMEM; 23742bd5306aSMaciej Matraszek 23752bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 23762bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 23772bd5306aSMaciej Matraszek if (!d) 23782bd5306aSMaciej Matraszek return -ENOMEM; 23792bd5306aSMaciej Matraszek 23802bd5306aSMaciej Matraszek return 0; 23812bd5306aSMaciej Matraszek } 23822bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 23832bd5306aSMaciej Matraszek 23842bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 23852bd5306aSMaciej Matraszek { 23862bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 23872bd5306aSMaciej Matraszek } 23882bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 23898b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 2390