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 42446d999cSRussell King /* 43446d999cSRussell King * Get the generic PM domain for a particular struct device. 44446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 45446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 46446d999cSRussell King * Any failure results in NULL being returned. 47446d999cSRussell King */ 48446d999cSRussell King struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) 49446d999cSRussell King { 50446d999cSRussell King struct generic_pm_domain *genpd = NULL, *gpd; 51446d999cSRussell King 52446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 53446d999cSRussell King return NULL; 54446d999cSRussell King 55446d999cSRussell King mutex_lock(&gpd_list_lock); 56446d999cSRussell King list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 57446d999cSRussell King if (&gpd->domain == dev->pm_domain) { 58446d999cSRussell King genpd = gpd; 59446d999cSRussell King break; 60446d999cSRussell King } 61446d999cSRussell King } 62446d999cSRussell King mutex_unlock(&gpd_list_lock); 63446d999cSRussell King 64446d999cSRussell King return genpd; 65446d999cSRussell King } 66446d999cSRussell King 67446d999cSRussell King /* 68446d999cSRussell King * This should only be used where we are certain that the pm_domain 69446d999cSRussell King * attached to the device is a genpd domain. 70446d999cSRussell King */ 71446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 725248051bSRafael J. Wysocki { 735248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 745248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 755248051bSRafael J. Wysocki 76596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 775248051bSRafael J. Wysocki } 78f721889fSRafael J. Wysocki 792b1d88cdSUlf Hansson static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) 80d5e4cbfeSRafael J. Wysocki { 8151cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 82d5e4cbfeSRafael J. Wysocki } 83d5e4cbfeSRafael J. Wysocki 842b1d88cdSUlf Hansson static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) 85d5e4cbfeSRafael J. Wysocki { 86ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 87d5e4cbfeSRafael J. Wysocki } 88d5e4cbfeSRafael J. Wysocki 89c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 90f721889fSRafael J. Wysocki { 91c4bb3160SRafael J. Wysocki bool ret = false; 92c4bb3160SRafael J. Wysocki 93c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 94c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 95c4bb3160SRafael J. Wysocki 96c4bb3160SRafael J. Wysocki return ret; 97c4bb3160SRafael J. Wysocki } 98c4bb3160SRafael J. Wysocki 99c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 100c4bb3160SRafael J. Wysocki { 101c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 1024e857c58SPeter Zijlstra smp_mb__after_atomic(); 103f721889fSRafael J. Wysocki } 104f721889fSRafael J. Wysocki 105a4630c61SGeert Uytterhoeven static int genpd_power_on(struct generic_pm_domain *genpd, bool timed) 106c8f0ea45SGeert Uytterhoeven { 107fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 108c8f0ea45SGeert Uytterhoeven ktime_t time_start; 109c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 110c8f0ea45SGeert Uytterhoeven int ret; 111c8f0ea45SGeert Uytterhoeven 112c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 113c8f0ea45SGeert Uytterhoeven return 0; 114c8f0ea45SGeert Uytterhoeven 115a4630c61SGeert Uytterhoeven if (!timed) 116a4630c61SGeert Uytterhoeven return genpd->power_on(genpd); 117a4630c61SGeert Uytterhoeven 118c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 119c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 120c8f0ea45SGeert Uytterhoeven if (ret) 121c8f0ea45SGeert Uytterhoeven return ret; 122c8f0ea45SGeert Uytterhoeven 123c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 124fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 125c8f0ea45SGeert Uytterhoeven return ret; 126c8f0ea45SGeert Uytterhoeven 127fc5cbf0cSAxel Haslam genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 128c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 1296d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 130c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 131c8f0ea45SGeert Uytterhoeven 132c8f0ea45SGeert Uytterhoeven return ret; 133c8f0ea45SGeert Uytterhoeven } 134c8f0ea45SGeert Uytterhoeven 135a4630c61SGeert Uytterhoeven static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) 136c8f0ea45SGeert Uytterhoeven { 137fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 138c8f0ea45SGeert Uytterhoeven ktime_t time_start; 139c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 140c8f0ea45SGeert Uytterhoeven int ret; 141c8f0ea45SGeert Uytterhoeven 142c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 143c8f0ea45SGeert Uytterhoeven return 0; 144c8f0ea45SGeert Uytterhoeven 145a4630c61SGeert Uytterhoeven if (!timed) 146a4630c61SGeert Uytterhoeven return genpd->power_off(genpd); 147a4630c61SGeert Uytterhoeven 148c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 149c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 150c8f0ea45SGeert Uytterhoeven if (ret == -EBUSY) 151c8f0ea45SGeert Uytterhoeven return ret; 152c8f0ea45SGeert Uytterhoeven 153c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 154fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 155c8f0ea45SGeert Uytterhoeven return ret; 156c8f0ea45SGeert Uytterhoeven 157fc5cbf0cSAxel Haslam genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 158c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 1596d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 160c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 161c8f0ea45SGeert Uytterhoeven 162c8f0ea45SGeert Uytterhoeven return ret; 163c8f0ea45SGeert Uytterhoeven } 164c8f0ea45SGeert Uytterhoeven 165f721889fSRafael J. Wysocki /** 1667420aa4fSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff(). 167a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 16829e47e21SUlf Hansson * 1697420aa4fSUlf Hansson * Queue up the execution of genpd_poweroff() unless it's already been done 17029e47e21SUlf Hansson * before. 17129e47e21SUlf Hansson */ 17229e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 17329e47e21SUlf Hansson { 17429e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 17529e47e21SUlf Hansson } 17629e47e21SUlf Hansson 17729e47e21SUlf Hansson /** 17853af16f7SUlf Hansson * genpd_poweron - Restore power to a given PM domain and its masters. 1795248051bSRafael J. Wysocki * @genpd: PM domain to power up. 1800106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 1815248051bSRafael J. Wysocki * 1825063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 1835248051bSRafael J. Wysocki * resume a device belonging to it. 1845248051bSRafael J. Wysocki */ 18553af16f7SUlf Hansson static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth) 1865248051bSRafael J. Wysocki { 1875063ce15SRafael J. Wysocki struct gpd_link *link; 1885248051bSRafael J. Wysocki int ret = 0; 1895248051bSRafael J. Wysocki 19017b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 191596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1923f241775SRafael J. Wysocki return 0; 1935248051bSRafael J. Wysocki 1945063ce15SRafael J. Wysocki /* 1955063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 1965063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 1975063ce15SRafael J. Wysocki * with it. 1985063ce15SRafael J. Wysocki */ 1995063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 2000106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 2015248051bSRafael J. Wysocki 2020106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 2030106ef51SMarek Szyprowski 2040106ef51SMarek Szyprowski mutex_lock_nested(&master->lock, depth + 1); 20553af16f7SUlf Hansson ret = genpd_poweron(master, depth + 1); 2060106ef51SMarek Szyprowski mutex_unlock(&master->lock); 2070106ef51SMarek Szyprowski 2085063ce15SRafael J. Wysocki if (ret) { 2090106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 2109e08cf42SRafael J. Wysocki goto err; 2115248051bSRafael J. Wysocki } 2125063ce15SRafael J. Wysocki } 2135248051bSRafael J. Wysocki 214a4630c61SGeert Uytterhoeven ret = genpd_power_on(genpd, true); 2159e08cf42SRafael J. Wysocki if (ret) 2169e08cf42SRafael J. Wysocki goto err; 2170140d8bdSRafael J. Wysocki 218ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 2193f241775SRafael J. Wysocki return 0; 2209e08cf42SRafael J. Wysocki 2219e08cf42SRafael J. Wysocki err: 22229e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 22329e47e21SUlf Hansson &genpd->slave_links, 22429e47e21SUlf Hansson slave_node) { 2255063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 22629e47e21SUlf Hansson genpd_queue_power_off_work(link->master); 22729e47e21SUlf Hansson } 2289e08cf42SRafael J. Wysocki 2293f241775SRafael J. Wysocki return ret; 2303f241775SRafael J. Wysocki } 2313f241775SRafael J. Wysocki 2328e9afafdSRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) 2338e9afafdSRafael J. Wysocki { 2342b1d88cdSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, save_state, dev); 2358e9afafdSRafael J. Wysocki } 2368e9afafdSRafael J. Wysocki 237ba2bbfbfSUlf Hansson static int genpd_restore_dev(struct generic_pm_domain *genpd, 2382b1d88cdSUlf Hansson struct device *dev) 2398e9afafdSRafael J. Wysocki { 240ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); 2418e9afafdSRafael J. Wysocki } 2428e9afafdSRafael J. Wysocki 2436ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 2446ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 2456ff7bb0dSRafael J. Wysocki { 2466ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 2476ff7bb0dSRafael J. Wysocki struct device *dev; 2486ff7bb0dSRafael J. Wysocki 2496ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 2506ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 2516ff7bb0dSRafael J. Wysocki 2526ff7bb0dSRafael J. Wysocki for (;;) { 2536ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 2546ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 2556ff7bb0dSRafael J. Wysocki 2566ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 2576ff7bb0dSRafael J. Wysocki 2586ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 2596ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 2601d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 2616ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 2626ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 2636ff7bb0dSRafael J. Wysocki } else { 2646ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 2656ff7bb0dSRafael J. Wysocki } 2666ff7bb0dSRafael J. Wysocki 2676ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 2686ff7bb0dSRafael J. Wysocki 2696ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 2706ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 2716ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2726ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 2736ff7bb0dSRafael J. Wysocki } 2746ff7bb0dSRafael J. Wysocki 2756ff7bb0dSRafael J. Wysocki dev = dev->parent; 2766ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 2776ff7bb0dSRafael J. Wysocki break; 2786ff7bb0dSRafael J. Wysocki } 2796ff7bb0dSRafael J. Wysocki 2806ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2816ff7bb0dSRafael J. Wysocki } 2826ff7bb0dSRafael J. Wysocki 2835248051bSRafael J. Wysocki /** 2847420aa4fSUlf Hansson * genpd_poweroff - Remove power from a given PM domain. 285f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 286f96b3c4fSUlf Hansson * @is_async: PM domain is powered down from a scheduled work 287f721889fSRafael J. Wysocki * 288f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 289ba2bbfbfSUlf Hansson * have been powered down, remove power from @genpd. 290f721889fSRafael J. Wysocki */ 2917420aa4fSUlf Hansson static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) 292f721889fSRafael J. Wysocki { 2934605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 2945063ce15SRafael J. Wysocki struct gpd_link *link; 295ba2bbfbfSUlf Hansson unsigned int not_suspended = 0; 296f721889fSRafael J. Wysocki 297c6d22b37SRafael J. Wysocki /* 298c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 299c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 300ba2bbfbfSUlf Hansson * (2) System suspend is in progress. 301c6d22b37SRafael J. Wysocki */ 3023f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 303ba2bbfbfSUlf Hansson || genpd->prepared_count > 0) 304f721889fSRafael J. Wysocki return 0; 305f721889fSRafael J. Wysocki 306c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 307f721889fSRafael J. Wysocki return -EBUSY; 308f721889fSRafael J. Wysocki 30934b1f762SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) { 31034b1f762SRafael J. Wysocki enum pm_qos_flags_status stat; 31134b1f762SRafael J. Wysocki 31234b1f762SRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, 31334b1f762SRafael J. Wysocki PM_QOS_FLAG_NO_POWER_OFF 31434b1f762SRafael J. Wysocki | PM_QOS_FLAG_REMOTE_WAKEUP); 31534b1f762SRafael J. Wysocki if (stat > PM_QOS_FLAGS_NONE) 31634b1f762SRafael J. Wysocki return -EBUSY; 31734b1f762SRafael J. Wysocki 318298cd0f0SLina Iyer if (!pm_runtime_suspended(pdd->dev) || pdd->dev->power.irq_safe) 319f721889fSRafael J. Wysocki not_suspended++; 32034b1f762SRafael J. Wysocki } 321f721889fSRafael J. Wysocki 322f96b3c4fSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && is_async)) 323f721889fSRafael J. Wysocki return -EBUSY; 324f721889fSRafael J. Wysocki 325f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 326f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 327f721889fSRafael J. Wysocki return -EAGAIN; 328f721889fSRafael J. Wysocki } 329f721889fSRafael J. Wysocki 3303c07cbc4SRafael J. Wysocki if (genpd->power_off) { 331ba2bbfbfSUlf Hansson int ret; 332ba2bbfbfSUlf Hansson 333ba2bbfbfSUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 334ba2bbfbfSUlf Hansson return -EBUSY; 33517b75ecaSRafael J. Wysocki 3363c07cbc4SRafael J. Wysocki /* 3375063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 3387420aa4fSUlf Hansson * managed to call genpd_poweron() for the master yet after 3397420aa4fSUlf Hansson * incrementing it. In that case genpd_poweron() will wait 3403c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 3417420aa4fSUlf Hansson * the genpd_poweron() restore power for us (this shouldn't 3423c07cbc4SRafael J. Wysocki * happen very often). 3433c07cbc4SRafael J. Wysocki */ 344a4630c61SGeert Uytterhoeven ret = genpd_power_off(genpd, true); 345ba2bbfbfSUlf Hansson if (ret) 346ba2bbfbfSUlf Hansson return ret; 347d2805402SRafael J. Wysocki } 348f721889fSRafael J. Wysocki 34917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 350221e9b58SRafael J. Wysocki 3515063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 3525063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 3535063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 3545063ce15SRafael J. Wysocki } 35517b75ecaSRafael J. Wysocki 356ba2bbfbfSUlf Hansson return 0; 357f721889fSRafael J. Wysocki } 358f721889fSRafael J. Wysocki 359f721889fSRafael J. Wysocki /** 360f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 361f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 362f721889fSRafael J. Wysocki */ 363f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 364f721889fSRafael J. Wysocki { 365f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 366f721889fSRafael J. Wysocki 367f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 368f721889fSRafael J. Wysocki 369ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 3707420aa4fSUlf Hansson genpd_poweroff(genpd, true); 371ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 372f721889fSRafael J. Wysocki } 373f721889fSRafael J. Wysocki 374f721889fSRafael J. Wysocki /** 375f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 376f721889fSRafael J. Wysocki * @dev: Device to suspend. 377f721889fSRafael J. Wysocki * 378f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 379f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 380f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 381f721889fSRafael J. Wysocki */ 382f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 383f721889fSRafael J. Wysocki { 384f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 385b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 3862b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 387ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 3882b1d88cdSUlf Hansson ktime_t time_start; 3892b1d88cdSUlf Hansson s64 elapsed_ns; 390d5e4cbfeSRafael J. Wysocki int ret; 391f721889fSRafael J. Wysocki 392f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 393f721889fSRafael J. Wysocki 3945248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 3955248051bSRafael J. Wysocki if (IS_ERR(genpd)) 396f721889fSRafael J. Wysocki return -EINVAL; 397f721889fSRafael J. Wysocki 398ffe12855SUlf Hansson /* 399ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 400ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 401ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 402ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 403ffe12855SUlf Hansson */ 404b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 405ffe12855SUlf Hansson if (runtime_pm && stop_ok && !stop_ok(dev)) 406b02c999aSRafael J. Wysocki return -EBUSY; 407b02c999aSRafael J. Wysocki 4082b1d88cdSUlf Hansson /* Measure suspend latency. */ 409ffe12855SUlf Hansson if (runtime_pm) 4102b1d88cdSUlf Hansson time_start = ktime_get(); 4112b1d88cdSUlf Hansson 412ba2bbfbfSUlf Hansson ret = genpd_save_dev(genpd, dev); 413f721889fSRafael J. Wysocki if (ret) 41417b75ecaSRafael J. Wysocki return ret; 41517b75ecaSRafael J. Wysocki 4162b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 417ba2bbfbfSUlf Hansson if (ret) { 4182b1d88cdSUlf Hansson genpd_restore_dev(genpd, dev); 419ba2bbfbfSUlf Hansson return ret; 420ba2bbfbfSUlf Hansson } 421ba2bbfbfSUlf Hansson 4222b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 423ffe12855SUlf Hansson if (runtime_pm) { 4242b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 4252b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 4262b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 4272b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 4282b1d88cdSUlf Hansson elapsed_ns); 4292b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 4302b1d88cdSUlf Hansson td->constraint_changed = true; 4312b1d88cdSUlf Hansson } 432ffe12855SUlf Hansson } 4332b1d88cdSUlf Hansson 4340aa2a221SRafael J. Wysocki /* 4350aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 4360aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 4370aa2a221SRafael J. Wysocki */ 4380aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 4390aa2a221SRafael J. Wysocki return 0; 4400aa2a221SRafael J. Wysocki 441c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 4427420aa4fSUlf Hansson genpd_poweroff(genpd, false); 443c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 444f721889fSRafael J. Wysocki 445f721889fSRafael J. Wysocki return 0; 446f721889fSRafael J. Wysocki } 447f721889fSRafael J. Wysocki 448f721889fSRafael J. Wysocki /** 449f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 450f721889fSRafael J. Wysocki * @dev: Device to resume. 451f721889fSRafael J. Wysocki * 452f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 453f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 454f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 455f721889fSRafael J. Wysocki */ 456f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 457f721889fSRafael J. Wysocki { 458f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 4592b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 460ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 4612b1d88cdSUlf Hansson ktime_t time_start; 4622b1d88cdSUlf Hansson s64 elapsed_ns; 463f721889fSRafael J. Wysocki int ret; 464ba2bbfbfSUlf Hansson bool timed = true; 465f721889fSRafael J. Wysocki 466f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 467f721889fSRafael J. Wysocki 4685248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4695248051bSRafael J. Wysocki if (IS_ERR(genpd)) 470f721889fSRafael J. Wysocki return -EINVAL; 471f721889fSRafael J. Wysocki 4720aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 473ba2bbfbfSUlf Hansson if (dev->power.irq_safe) { 474ba2bbfbfSUlf Hansson timed = false; 475ba2bbfbfSUlf Hansson goto out; 476ba2bbfbfSUlf Hansson } 4770aa2a221SRafael J. Wysocki 478c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 47953af16f7SUlf Hansson ret = genpd_poweron(genpd, 0); 4803f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 481ba2bbfbfSUlf Hansson 482ba2bbfbfSUlf Hansson if (ret) 4833f241775SRafael J. Wysocki return ret; 484c6d22b37SRafael J. Wysocki 485ba2bbfbfSUlf Hansson out: 4862b1d88cdSUlf Hansson /* Measure resume latency. */ 487ffe12855SUlf Hansson if (timed && runtime_pm) 4882b1d88cdSUlf Hansson time_start = ktime_get(); 4892b1d88cdSUlf Hansson 490076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 491076395caSLaurent Pinchart if (ret) 492076395caSLaurent Pinchart goto err_poweroff; 493076395caSLaurent Pinchart 494076395caSLaurent Pinchart ret = genpd_restore_dev(genpd, dev); 495076395caSLaurent Pinchart if (ret) 496076395caSLaurent Pinchart goto err_stop; 4972b1d88cdSUlf Hansson 4982b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 499ffe12855SUlf Hansson if (timed && runtime_pm) { 5002b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5012b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 5022b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 5032b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 5042b1d88cdSUlf Hansson elapsed_ns); 5052b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5062b1d88cdSUlf Hansson td->constraint_changed = true; 5072b1d88cdSUlf Hansson } 5082b1d88cdSUlf Hansson } 50917b75ecaSRafael J. Wysocki 510f721889fSRafael J. Wysocki return 0; 511076395caSLaurent Pinchart 512076395caSLaurent Pinchart err_stop: 513076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 514076395caSLaurent Pinchart err_poweroff: 515076395caSLaurent Pinchart if (!dev->power.irq_safe) { 516076395caSLaurent Pinchart mutex_lock(&genpd->lock); 517076395caSLaurent Pinchart genpd_poweroff(genpd, 0); 518076395caSLaurent Pinchart mutex_unlock(&genpd->lock); 519076395caSLaurent Pinchart } 520076395caSLaurent Pinchart 521076395caSLaurent Pinchart return ret; 522f721889fSRafael J. Wysocki } 523f721889fSRafael J. Wysocki 52439ac5ba5STushar Behera static bool pd_ignore_unused; 52539ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 52639ac5ba5STushar Behera { 52739ac5ba5STushar Behera pd_ignore_unused = true; 52839ac5ba5STushar Behera return 1; 52939ac5ba5STushar Behera } 53039ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 53139ac5ba5STushar Behera 53217f2ae7fSRafael J. Wysocki /** 533bb4b72fcSUlf Hansson * genpd_poweroff_unused - Power off all PM domains with no devices in use. 53417f2ae7fSRafael J. Wysocki */ 535bb4b72fcSUlf Hansson static int __init genpd_poweroff_unused(void) 53617f2ae7fSRafael J. Wysocki { 53717f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 53817f2ae7fSRafael J. Wysocki 53939ac5ba5STushar Behera if (pd_ignore_unused) { 54039ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 541bb4b72fcSUlf Hansson return 0; 54239ac5ba5STushar Behera } 54339ac5ba5STushar Behera 54417f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 54517f2ae7fSRafael J. Wysocki 54617f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 54717f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 54817f2ae7fSRafael J. Wysocki 54917f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 55017f2ae7fSRafael J. Wysocki 5512fe71dcdSUlf Hansson return 0; 5522fe71dcdSUlf Hansson } 5532fe71dcdSUlf Hansson late_initcall(genpd_poweroff_unused); 5542fe71dcdSUlf Hansson 555596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 556596ba34bSRafael J. Wysocki 55777f827deSRafael J. Wysocki /** 55877f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 55977f827deSRafael J. Wysocki * @genpd: PM domain to check. 56077f827deSRafael J. Wysocki */ 561895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 56277f827deSRafael J. Wysocki { 563895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 56477f827deSRafael J. Wysocki 56577f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 56677f827deSRafael J. Wysocki return false; 56777f827deSRafael J. Wysocki 56877f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 56977f827deSRafael J. Wysocki if (gpd == genpd) 57077f827deSRafael J. Wysocki return true; 57177f827deSRafael J. Wysocki 57277f827deSRafael J. Wysocki return false; 57377f827deSRafael J. Wysocki } 57477f827deSRafael J. Wysocki 575d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 576d5e4cbfeSRafael J. Wysocki struct device *dev) 577d5e4cbfeSRafael J. Wysocki { 578d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 579d5e4cbfeSRafael J. Wysocki } 580d5e4cbfeSRafael J. Wysocki 581596ba34bSRafael J. Wysocki /** 5825063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 583596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 584a4630c61SGeert Uytterhoeven * @timed: True if latency measurements are allowed. 585596ba34bSRafael J. Wysocki * 586596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 5875063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 588596ba34bSRafael J. Wysocki * 58977f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 59077f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 59177f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 59277f827deSRafael J. Wysocki * parallel). 593596ba34bSRafael J. Wysocki */ 594a4630c61SGeert Uytterhoeven static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, 595a4630c61SGeert Uytterhoeven bool timed) 596596ba34bSRafael J. Wysocki { 5975063ce15SRafael J. Wysocki struct gpd_link *link; 598596ba34bSRafael J. Wysocki 59917b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 600596ba34bSRafael J. Wysocki return; 601596ba34bSRafael J. Wysocki 602c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 603c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 604596ba34bSRafael J. Wysocki return; 605596ba34bSRafael J. Wysocki 606fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 607fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 608a4630c61SGeert Uytterhoeven genpd_power_off(genpd, timed); 609596ba34bSRafael J. Wysocki 61017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 6115063ce15SRafael J. Wysocki 6125063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 6135063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 614a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(link->master, timed); 615596ba34bSRafael J. Wysocki } 616596ba34bSRafael J. Wysocki } 617596ba34bSRafael J. Wysocki 618596ba34bSRafael J. Wysocki /** 619802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 620802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 621a4630c61SGeert Uytterhoeven * @timed: True if latency measurements are allowed. 622802d8b49SRafael J. Wysocki * 62377f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 62477f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 62577f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 62677f827deSRafael J. Wysocki * parallel). 627802d8b49SRafael J. Wysocki */ 628a4630c61SGeert Uytterhoeven static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd, 629a4630c61SGeert Uytterhoeven bool timed) 630802d8b49SRafael J. Wysocki { 631802d8b49SRafael J. Wysocki struct gpd_link *link; 632802d8b49SRafael J. Wysocki 633ba2bbfbfSUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 634802d8b49SRafael J. Wysocki return; 635802d8b49SRafael J. Wysocki 636802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 637a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(link->master, timed); 638802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 639802d8b49SRafael J. Wysocki } 640802d8b49SRafael J. Wysocki 641a4630c61SGeert Uytterhoeven genpd_power_on(genpd, timed); 642802d8b49SRafael J. Wysocki 643802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 644802d8b49SRafael J. Wysocki } 645802d8b49SRafael J. Wysocki 646802d8b49SRafael J. Wysocki /** 6474ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 6484ecd6e65SRafael J. Wysocki * @dev: Device to check. 6494ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 6504ecd6e65SRafael J. Wysocki * 6514ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 6524ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 6534ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 6544ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 6554ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 6564ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 6574ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 6584ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 6594ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 6604ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 6614ecd6e65SRafael J. Wysocki */ 6624ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 6634ecd6e65SRafael J. Wysocki { 6644ecd6e65SRafael J. Wysocki bool active_wakeup; 6654ecd6e65SRafael J. Wysocki 6664ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 6674ecd6e65SRafael J. Wysocki return false; 6684ecd6e65SRafael J. Wysocki 669d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 6704ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 6714ecd6e65SRafael J. Wysocki } 6724ecd6e65SRafael J. Wysocki 6734ecd6e65SRafael J. Wysocki /** 674596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 675596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 676596ba34bSRafael J. Wysocki * 677596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 678596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 679596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 680596ba34bSRafael J. Wysocki * consisting of I/O devices. 681596ba34bSRafael J. Wysocki */ 682596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 683596ba34bSRafael J. Wysocki { 684596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 685b6c10c84SRafael J. Wysocki int ret; 686596ba34bSRafael J. Wysocki 687596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 688596ba34bSRafael J. Wysocki 689596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 690596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 691596ba34bSRafael J. Wysocki return -EINVAL; 692596ba34bSRafael J. Wysocki 69317b75ecaSRafael J. Wysocki /* 69417b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 69517b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 69617b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 69717b75ecaSRafael J. Wysocki */ 69817b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 69917b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 70017b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 70117b75ecaSRafael J. Wysocki 70217b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 70384167035SUlf Hansson pm_runtime_put(dev); 70417b75ecaSRafael J. Wysocki return -EBUSY; 70517b75ecaSRafael J. Wysocki } 70617b75ecaSRafael J. Wysocki 7074ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 7084ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 7094ecd6e65SRafael J. Wysocki 710ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 711596ba34bSRafael J. Wysocki 71265533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 71365533bbfSRafael J. Wysocki genpd->suspended_count = 0; 71417b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 71565533bbfSRafael J. Wysocki } 71617b75ecaSRafael J. Wysocki 717ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 718596ba34bSRafael J. Wysocki 719596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 72017b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 721596ba34bSRafael J. Wysocki return 0; 722596ba34bSRafael J. Wysocki } 723596ba34bSRafael J. Wysocki 724596ba34bSRafael J. Wysocki /* 72517b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 7267420aa4fSUlf Hansson * so genpd_poweron() will return immediately, but if the device 727d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 72817b75ecaSRafael J. Wysocki * to make it operational. 729596ba34bSRafael J. Wysocki */ 73017b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 731596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 732596ba34bSRafael J. Wysocki 733b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 734b6c10c84SRafael J. Wysocki if (ret) { 735b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 736b6c10c84SRafael J. Wysocki 737b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 738b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 739b6c10c84SRafael J. Wysocki 740b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 74117b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 742b6c10c84SRafael J. Wysocki } 74317b75ecaSRafael J. Wysocki 74484167035SUlf Hansson pm_runtime_put(dev); 745b6c10c84SRafael J. Wysocki return ret; 746596ba34bSRafael J. Wysocki } 747596ba34bSRafael J. Wysocki 748596ba34bSRafael J. Wysocki /** 749596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 750596ba34bSRafael J. Wysocki * @dev: Device to suspend. 751596ba34bSRafael J. Wysocki * 752596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 753596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 754596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 755596ba34bSRafael J. Wysocki */ 756596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 757596ba34bSRafael J. Wysocki { 758596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 759596ba34bSRafael J. Wysocki 760596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 761596ba34bSRafael J. Wysocki 762596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 763596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 764596ba34bSRafael J. Wysocki return -EINVAL; 765596ba34bSRafael J. Wysocki 7661e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 767596ba34bSRafael J. Wysocki } 768596ba34bSRafael J. Wysocki 769596ba34bSRafael J. Wysocki /** 7700496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 771596ba34bSRafael J. Wysocki * @dev: Device to suspend. 772596ba34bSRafael J. Wysocki * 773596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 774596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 775596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 776596ba34bSRafael J. Wysocki */ 7770496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 778596ba34bSRafael J. Wysocki { 779596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 780596ba34bSRafael J. Wysocki 781596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 782596ba34bSRafael J. Wysocki 783596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 784596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 785596ba34bSRafael J. Wysocki return -EINVAL; 786596ba34bSRafael J. Wysocki 7871e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_suspend_late(dev); 7880496c8aeSRafael J. Wysocki } 789596ba34bSRafael J. Wysocki 7900496c8aeSRafael J. Wysocki /** 7910496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 7920496c8aeSRafael J. Wysocki * @dev: Device to suspend. 7930496c8aeSRafael J. Wysocki * 7940496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 7950496c8aeSRafael J. Wysocki * been stopped. 7960496c8aeSRafael J. Wysocki */ 7970496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 7980496c8aeSRafael J. Wysocki { 7990496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 800596ba34bSRafael J. Wysocki 8010496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8020496c8aeSRafael J. Wysocki 8030496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 8040496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 8050496c8aeSRafael J. Wysocki return -EINVAL; 8060496c8aeSRafael J. Wysocki 807dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 8080496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 809d4f2d87aSRafael J. Wysocki return 0; 810d4f2d87aSRafael J. Wysocki 8112b1d88cdSUlf Hansson genpd_stop_dev(genpd, dev); 812596ba34bSRafael J. Wysocki 813596ba34bSRafael J. Wysocki /* 814596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 815596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 816596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 817596ba34bSRafael J. Wysocki */ 818596ba34bSRafael J. Wysocki genpd->suspended_count++; 819a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(genpd, true); 820596ba34bSRafael J. Wysocki 821596ba34bSRafael J. Wysocki return 0; 822596ba34bSRafael J. Wysocki } 823596ba34bSRafael J. Wysocki 824596ba34bSRafael J. Wysocki /** 8250496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 826596ba34bSRafael J. Wysocki * @dev: Device to resume. 827596ba34bSRafael J. Wysocki * 8280496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 829596ba34bSRafael J. Wysocki */ 830596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 831596ba34bSRafael J. Wysocki { 832596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 833596ba34bSRafael J. Wysocki 834596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 835596ba34bSRafael J. Wysocki 836596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 837596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 838596ba34bSRafael J. Wysocki return -EINVAL; 839596ba34bSRafael J. Wysocki 840dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 841cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 842596ba34bSRafael J. Wysocki return 0; 843596ba34bSRafael J. Wysocki 844596ba34bSRafael J. Wysocki /* 845596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 846596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 847596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 848596ba34bSRafael J. Wysocki */ 849a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, true); 850596ba34bSRafael J. Wysocki genpd->suspended_count--; 851596ba34bSRafael J. Wysocki 8522b1d88cdSUlf Hansson return genpd_start_dev(genpd, dev); 853596ba34bSRafael J. Wysocki } 854596ba34bSRafael J. Wysocki 855596ba34bSRafael J. Wysocki /** 8560496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 8570496c8aeSRafael J. Wysocki * @dev: Device to resume. 8580496c8aeSRafael J. Wysocki * 8590496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 8600496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 8610496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 8620496c8aeSRafael J. Wysocki * devices. 8630496c8aeSRafael J. Wysocki */ 8640496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 8650496c8aeSRafael J. Wysocki { 8660496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 8670496c8aeSRafael J. Wysocki 8680496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8690496c8aeSRafael J. Wysocki 8700496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 8710496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 8720496c8aeSRafael J. Wysocki return -EINVAL; 8730496c8aeSRafael J. Wysocki 8741e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_resume_early(dev); 8750496c8aeSRafael J. Wysocki } 8760496c8aeSRafael J. Wysocki 8770496c8aeSRafael J. Wysocki /** 8780496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 879596ba34bSRafael J. Wysocki * @dev: Device to resume. 880596ba34bSRafael J. Wysocki * 881596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 882596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 883596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 884596ba34bSRafael J. Wysocki */ 885596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 886596ba34bSRafael J. Wysocki { 887596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 888596ba34bSRafael J. Wysocki 889596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 890596ba34bSRafael J. Wysocki 891596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 892596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 893596ba34bSRafael J. Wysocki return -EINVAL; 894596ba34bSRafael J. Wysocki 8951e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 896596ba34bSRafael J. Wysocki } 897596ba34bSRafael J. Wysocki 898596ba34bSRafael J. Wysocki /** 8990496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 900596ba34bSRafael J. Wysocki * @dev: Device to freeze. 901596ba34bSRafael J. Wysocki * 902596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 903596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 904596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 905596ba34bSRafael J. Wysocki */ 906596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 907596ba34bSRafael J. Wysocki { 908596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 909596ba34bSRafael J. Wysocki 910596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 911596ba34bSRafael J. Wysocki 912596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 913596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 914596ba34bSRafael J. Wysocki return -EINVAL; 915596ba34bSRafael J. Wysocki 9161e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 917596ba34bSRafael J. Wysocki } 918596ba34bSRafael J. Wysocki 919596ba34bSRafael J. Wysocki /** 9200496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 9210496c8aeSRafael J. Wysocki * @dev: Device to freeze. 9220496c8aeSRafael J. Wysocki * 9230496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 9240496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9250496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9260496c8aeSRafael J. Wysocki * devices. 9270496c8aeSRafael J. Wysocki */ 9280496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 9290496c8aeSRafael J. Wysocki { 9300496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 9310496c8aeSRafael J. Wysocki 9320496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9330496c8aeSRafael J. Wysocki 9340496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9350496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9360496c8aeSRafael J. Wysocki return -EINVAL; 9370496c8aeSRafael J. Wysocki 9381e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_freeze_late(dev); 9390496c8aeSRafael J. Wysocki } 9400496c8aeSRafael J. Wysocki 9410496c8aeSRafael J. Wysocki /** 9420496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 943596ba34bSRafael J. Wysocki * @dev: Device to freeze. 944596ba34bSRafael J. Wysocki * 945596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 946596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 947596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 948596ba34bSRafael J. Wysocki * devices. 949596ba34bSRafael J. Wysocki */ 950596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 951596ba34bSRafael J. Wysocki { 952596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 953596ba34bSRafael J. Wysocki 954596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 955596ba34bSRafael J. Wysocki 956596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 957596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 958596ba34bSRafael J. Wysocki return -EINVAL; 959596ba34bSRafael J. Wysocki 9602b1d88cdSUlf Hansson return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); 961596ba34bSRafael J. Wysocki } 962596ba34bSRafael J. Wysocki 963596ba34bSRafael J. Wysocki /** 9640496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 965596ba34bSRafael J. Wysocki * @dev: Device to thaw. 966596ba34bSRafael J. Wysocki * 9670496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 9680496c8aeSRafael J. Wysocki * before the system transition. 969596ba34bSRafael J. Wysocki */ 970596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 971596ba34bSRafael J. Wysocki { 972596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 973596ba34bSRafael J. Wysocki 974596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 975596ba34bSRafael J. Wysocki 976596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 977596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 978596ba34bSRafael J. Wysocki return -EINVAL; 979596ba34bSRafael J. Wysocki 98051cda844SUlf Hansson return genpd->suspend_power_off ? 9812b1d88cdSUlf Hansson 0 : genpd_start_dev(genpd, dev); 9820496c8aeSRafael J. Wysocki } 983596ba34bSRafael J. Wysocki 9840496c8aeSRafael J. Wysocki /** 9850496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 9860496c8aeSRafael J. Wysocki * @dev: Device to thaw. 9870496c8aeSRafael J. Wysocki * 9880496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 9890496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9900496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9910496c8aeSRafael J. Wysocki * devices. 9920496c8aeSRafael J. Wysocki */ 9930496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 9940496c8aeSRafael J. Wysocki { 9950496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 996596ba34bSRafael J. Wysocki 9970496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9980496c8aeSRafael J. Wysocki 9990496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10000496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10010496c8aeSRafael J. Wysocki return -EINVAL; 10020496c8aeSRafael J. Wysocki 10031e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_thaw_early(dev); 1004596ba34bSRafael J. Wysocki } 1005596ba34bSRafael J. Wysocki 1006596ba34bSRafael J. Wysocki /** 1007596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1008596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1009596ba34bSRafael J. Wysocki * 1010596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1011596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1012596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1013596ba34bSRafael J. Wysocki */ 1014596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1015596ba34bSRafael J. Wysocki { 1016596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1017596ba34bSRafael J. Wysocki 1018596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1019596ba34bSRafael J. Wysocki 1020596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1021596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1022596ba34bSRafael J. Wysocki return -EINVAL; 1023596ba34bSRafael J. Wysocki 10241e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 1025596ba34bSRafael J. Wysocki } 1026596ba34bSRafael J. Wysocki 1027596ba34bSRafael J. Wysocki /** 10280496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1029596ba34bSRafael J. Wysocki * @dev: Device to resume. 1030596ba34bSRafael J. Wysocki * 10310496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 10320496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1033596ba34bSRafael J. Wysocki */ 1034596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1035596ba34bSRafael J. Wysocki { 1036596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1037596ba34bSRafael J. Wysocki 1038596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1039596ba34bSRafael J. Wysocki 1040596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1041596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1042596ba34bSRafael J. Wysocki return -EINVAL; 1043596ba34bSRafael J. Wysocki 1044596ba34bSRafael J. Wysocki /* 1045596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1046596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1047596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 104865533bbfSRafael J. Wysocki * 104965533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 105065533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 105165533bbfSRafael J. Wysocki */ 105265533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 105365533bbfSRafael J. Wysocki /* 105465533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1055802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 1056802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1057596ba34bSRafael J. Wysocki */ 105817b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1059596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1060596ba34bSRafael J. Wysocki /* 106165533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 106265533bbfSRafael J. Wysocki * sure it will be off going forward. 1063596ba34bSRafael J. Wysocki */ 1064a4630c61SGeert Uytterhoeven genpd_power_off(genpd, true); 106565533bbfSRafael J. Wysocki 1066596ba34bSRafael J. Wysocki return 0; 1067596ba34bSRafael J. Wysocki } 106865533bbfSRafael J. Wysocki } 1069596ba34bSRafael J. Wysocki 107018dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 107118dd2eceSRafael J. Wysocki return 0; 107218dd2eceSRafael J. Wysocki 1073a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, true); 1074596ba34bSRafael J. Wysocki 10752b1d88cdSUlf Hansson return genpd_start_dev(genpd, dev); 1076596ba34bSRafael J. Wysocki } 1077596ba34bSRafael J. Wysocki 1078596ba34bSRafael J. Wysocki /** 1079596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1080596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1081596ba34bSRafael J. Wysocki * 1082596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1083596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1084596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1085596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1086596ba34bSRafael J. Wysocki */ 1087596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1088596ba34bSRafael J. Wysocki { 1089596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1090596ba34bSRafael J. Wysocki bool run_complete; 1091596ba34bSRafael J. Wysocki 1092596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1093596ba34bSRafael J. Wysocki 1094596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1095596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1096596ba34bSRafael J. Wysocki return; 1097596ba34bSRafael J. Wysocki 1098596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1099596ba34bSRafael J. Wysocki 1100596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1101596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1102596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1103596ba34bSRafael J. Wysocki 1104596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1105596ba34bSRafael J. Wysocki 1106596ba34bSRafael J. Wysocki if (run_complete) { 1107596ba34bSRafael J. Wysocki pm_generic_complete(dev); 11086f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1109596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 1110af939339SUlf Hansson pm_request_idle(dev); 1111596ba34bSRafael J. Wysocki } 1112596ba34bSRafael J. Wysocki } 1113596ba34bSRafael J. Wysocki 111477f827deSRafael J. Wysocki /** 1115d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 111677f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 111777f827deSRafael J. Wysocki * 111877f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 111977f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 112077f827deSRafael J. Wysocki */ 1121d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 112277f827deSRafael J. Wysocki { 112377f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 112477f827deSRafael J. Wysocki 112577f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 112677f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 112777f827deSRafael J. Wysocki return; 112877f827deSRafael J. Wysocki 112977f827deSRafael J. Wysocki if (suspend) { 113077f827deSRafael J. Wysocki genpd->suspended_count++; 1131a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(genpd, false); 113277f827deSRafael J. Wysocki } else { 1133a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, false); 113477f827deSRafael J. Wysocki genpd->suspended_count--; 113577f827deSRafael J. Wysocki } 113677f827deSRafael J. Wysocki } 1137d47e6464SUlf Hansson 1138d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1139d47e6464SUlf Hansson { 1140d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1141d47e6464SUlf Hansson } 1142d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1143d47e6464SUlf Hansson 1144d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1145d47e6464SUlf Hansson { 1146d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1147d47e6464SUlf Hansson } 1148d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 114977f827deSRafael J. Wysocki 1150d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1151596ba34bSRafael J. Wysocki 1152596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1153596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 11540496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1155596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 11560496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1157596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1158596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1159596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 11600496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1161596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 11620496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1163596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1164596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1165596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1166596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1167596ba34bSRafael J. Wysocki 1168596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1169596ba34bSRafael J. Wysocki 1170f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1171f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1172f104e1e5SUlf Hansson struct gpd_timing_data *td) 11731d5fcfecSRafael J. Wysocki { 11741d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11753e235685SUlf Hansson int ret; 11763e235685SUlf Hansson 11773e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11783e235685SUlf Hansson if (ret) 11793e235685SUlf Hansson return ERR_PTR(ret); 11801d5fcfecSRafael J. Wysocki 11811d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 11823e235685SUlf Hansson if (!gpd_data) { 11833e235685SUlf Hansson ret = -ENOMEM; 11843e235685SUlf Hansson goto err_put; 11853e235685SUlf Hansson } 11861d5fcfecSRafael J. Wysocki 1187f104e1e5SUlf Hansson if (td) 1188f104e1e5SUlf Hansson gpd_data->td = *td; 1189f104e1e5SUlf Hansson 1190f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1191f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1192f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1193f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1194f104e1e5SUlf Hansson 1195f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1196f104e1e5SUlf Hansson 1197f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1198f104e1e5SUlf Hansson ret = -EINVAL; 1199f104e1e5SUlf Hansson goto err_free; 1200f104e1e5SUlf Hansson } 1201f104e1e5SUlf Hansson 1202f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1203f104e1e5SUlf Hansson 1204f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1205f104e1e5SUlf Hansson 1206989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1207989561deSTomeu Vizoso 12081d5fcfecSRafael J. Wysocki return gpd_data; 12093e235685SUlf Hansson 1210f104e1e5SUlf Hansson err_free: 1211f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1212f104e1e5SUlf Hansson kfree(gpd_data); 12133e235685SUlf Hansson err_put: 12143e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12153e235685SUlf Hansson return ERR_PTR(ret); 12161d5fcfecSRafael J. Wysocki } 12171d5fcfecSRafael J. Wysocki 121849d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 12191d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 12201d5fcfecSRafael J. Wysocki { 1221989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1222989561deSTomeu Vizoso 1223f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1224f104e1e5SUlf Hansson 1225f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1226f104e1e5SUlf Hansson 1227f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1228f104e1e5SUlf Hansson 12291d5fcfecSRafael J. Wysocki kfree(gpd_data); 12303e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12311d5fcfecSRafael J. Wysocki } 12321d5fcfecSRafael J. Wysocki 1233f721889fSRafael J. Wysocki /** 1234b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1235f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1236f721889fSRafael J. Wysocki * @dev: Device to be added. 1237b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1238f721889fSRafael J. Wysocki */ 1239b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1240b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1241f721889fSRafael J. Wysocki { 1242c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1243f721889fSRafael J. Wysocki int ret = 0; 1244f721889fSRafael J. Wysocki 1245f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1246f721889fSRafael J. Wysocki 1247f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1248f721889fSRafael J. Wysocki return -EINVAL; 1249f721889fSRafael J. Wysocki 1250f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 12513e235685SUlf Hansson if (IS_ERR(gpd_data)) 12523e235685SUlf Hansson return PTR_ERR(gpd_data); 12536ff7bb0dSRafael J. Wysocki 1254ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1255f721889fSRafael J. Wysocki 1256596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1257596ba34bSRafael J. Wysocki ret = -EAGAIN; 1258596ba34bSRafael J. Wysocki goto out; 1259596ba34bSRafael J. Wysocki } 1260596ba34bSRafael J. Wysocki 1261b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1262b472c2faSUlf Hansson if (ret) 1263b472c2faSUlf Hansson goto out; 1264d79b6fe1SGeert Uytterhoeven 126514b53064SUlf Hansson genpd->device_count++; 126614b53064SUlf Hansson genpd->max_off_time_changed = true; 126714b53064SUlf Hansson 12681d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 12696ff7bb0dSRafael J. Wysocki 1270f721889fSRafael J. Wysocki out: 1271ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1272f721889fSRafael J. Wysocki 1273c0356db7SUlf Hansson if (ret) 1274c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1275c0356db7SUlf Hansson else 1276c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12771d5fcfecSRafael J. Wysocki 1278f721889fSRafael J. Wysocki return ret; 1279f721889fSRafael J. Wysocki } 128024c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1281f721889fSRafael J. Wysocki 1282f721889fSRafael J. Wysocki /** 1283f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1284f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1285f721889fSRafael J. Wysocki * @dev: Device to be removed. 1286f721889fSRafael J. Wysocki */ 1287f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1288f721889fSRafael J. Wysocki struct device *dev) 1289f721889fSRafael J. Wysocki { 12906ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12914605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1292efa69025SRafael J. Wysocki int ret = 0; 1293f721889fSRafael J. Wysocki 1294f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1295f721889fSRafael J. Wysocki 1296df6a0d6fSRussell King if (!genpd || genpd != pm_genpd_lookup_dev(dev)) 1297f721889fSRafael J. Wysocki return -EINVAL; 1298f721889fSRafael J. Wysocki 1299c0356db7SUlf Hansson /* The above validation also means we have existing domain_data. */ 1300c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1301c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1302c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1303c0356db7SUlf Hansson 1304ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1305f721889fSRafael J. Wysocki 1306596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1307596ba34bSRafael J. Wysocki ret = -EAGAIN; 1308596ba34bSRafael J. Wysocki goto out; 1309596ba34bSRafael J. Wysocki } 1310596ba34bSRafael J. Wysocki 13116ff7bb0dSRafael J. Wysocki genpd->device_count--; 13126ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 13136ff7bb0dSRafael J. Wysocki 1314d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1315c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1316d79b6fe1SGeert Uytterhoeven 1317efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 13186ff7bb0dSRafael J. Wysocki 1319ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 13206ff7bb0dSRafael J. Wysocki 132149d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 13221d5fcfecSRafael J. Wysocki 13236ff7bb0dSRafael J. Wysocki return 0; 1324f721889fSRafael J. Wysocki 1325596ba34bSRafael J. Wysocki out: 1326ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1327c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1328f721889fSRafael J. Wysocki 1329f721889fSRafael J. Wysocki return ret; 1330f721889fSRafael J. Wysocki } 133124c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1332f721889fSRafael J. Wysocki 1333f721889fSRafael J. Wysocki /** 1334f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1335f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1336bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1337f721889fSRafael J. Wysocki */ 1338f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1339bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1340f721889fSRafael J. Wysocki { 13412547923dSLina Iyer struct gpd_link *link, *itr; 1342f721889fSRafael J. Wysocki int ret = 0; 1343f721889fSRafael J. Wysocki 1344fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1345fb7268beSRafael J. Wysocki || genpd == subdomain) 1346f721889fSRafael J. Wysocki return -EINVAL; 1347f721889fSRafael J. Wysocki 13482547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13492547923dSLina Iyer if (!link) 13502547923dSLina Iyer return -ENOMEM; 13512547923dSLina Iyer 1352cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1353cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1354f721889fSRafael J. Wysocki 135517b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1356bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1357f721889fSRafael J. Wysocki ret = -EINVAL; 1358f721889fSRafael J. Wysocki goto out; 1359f721889fSRafael J. Wysocki } 1360f721889fSRafael J. Wysocki 13612547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 13622547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1363f721889fSRafael J. Wysocki ret = -EINVAL; 1364f721889fSRafael J. Wysocki goto out; 1365f721889fSRafael J. Wysocki } 1366f721889fSRafael J. Wysocki } 1367f721889fSRafael J. Wysocki 13685063ce15SRafael J. Wysocki link->master = genpd; 13695063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1370bc0403ffSRafael J. Wysocki link->slave = subdomain; 1371bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1372bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1373c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1374f721889fSRafael J. Wysocki 1375f721889fSRafael J. Wysocki out: 1376ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1377cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 13782547923dSLina Iyer if (ret) 13792547923dSLina Iyer kfree(link); 1380f721889fSRafael J. Wysocki return ret; 1381f721889fSRafael J. Wysocki } 1382d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1383f721889fSRafael J. Wysocki 1384f721889fSRafael J. Wysocki /** 1385f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1386f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 13875063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1388f721889fSRafael J. Wysocki */ 1389f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 13905063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1391f721889fSRafael J. Wysocki { 13925063ce15SRafael J. Wysocki struct gpd_link *link; 1393f721889fSRafael J. Wysocki int ret = -EINVAL; 1394f721889fSRafael J. Wysocki 13955063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1396f721889fSRafael J. Wysocki return -EINVAL; 1397f721889fSRafael J. Wysocki 1398cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1399cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1400f721889fSRafael J. Wysocki 1401beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 140230e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 140330e7a65bSJon Hunter subdomain->name); 140430e7a65bSJon Hunter ret = -EBUSY; 140530e7a65bSJon Hunter goto out; 140630e7a65bSJon Hunter } 140730e7a65bSJon Hunter 14085063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 14095063ce15SRafael J. Wysocki if (link->slave != subdomain) 1410f721889fSRafael J. Wysocki continue; 1411f721889fSRafael J. Wysocki 14125063ce15SRafael J. Wysocki list_del(&link->master_node); 14135063ce15SRafael J. Wysocki list_del(&link->slave_node); 14145063ce15SRafael J. Wysocki kfree(link); 141517b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1416f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1417f721889fSRafael J. Wysocki 1418f721889fSRafael J. Wysocki ret = 0; 1419f721889fSRafael J. Wysocki break; 1420f721889fSRafael J. Wysocki } 1421f721889fSRafael J. Wysocki 142230e7a65bSJon Hunter out: 1423ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1424cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 1425f721889fSRafael J. Wysocki 1426f721889fSRafael J. Wysocki return ret; 1427f721889fSRafael J. Wysocki } 1428d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1429f721889fSRafael J. Wysocki 1430d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1431d23b9b00SRafael J. Wysocki 1432d5e4cbfeSRafael J. Wysocki /** 143312e10bb6SGeert Uytterhoeven * pm_genpd_default_save_state - Default "save device state" for PM domains. 1434ecf00475SRafael J. Wysocki * @dev: Device to handle. 1435ecf00475SRafael J. Wysocki */ 1436ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1437ecf00475SRafael J. Wysocki { 1438ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1439ecf00475SRafael J. Wysocki 14400b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 14410b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 14420b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 14430b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 14440b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 14450b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 14460b589741SRafael J. Wysocki else 14470b589741SRafael J. Wysocki cb = NULL; 1448ecf00475SRafael J. Wysocki 14490b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 14500b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 14510b589741SRafael J. Wysocki 14520b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1453ecf00475SRafael J. Wysocki } 1454ecf00475SRafael J. Wysocki 1455ecf00475SRafael J. Wysocki /** 145612e10bb6SGeert Uytterhoeven * pm_genpd_default_restore_state - Default PM domains "restore device state". 1457ecf00475SRafael J. Wysocki * @dev: Device to handle. 1458ecf00475SRafael J. Wysocki */ 1459ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1460ecf00475SRafael J. Wysocki { 1461ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1462ecf00475SRafael J. Wysocki 14630b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 14640b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 14650b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 14660b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 14670b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 14680b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 14690b589741SRafael J. Wysocki else 14700b589741SRafael J. Wysocki cb = NULL; 1471ecf00475SRafael J. Wysocki 14720b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 14730b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 14740b589741SRafael J. Wysocki 14750b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1476ecf00475SRafael J. Wysocki } 1477ecf00475SRafael J. Wysocki 1478d23b9b00SRafael J. Wysocki /** 1479f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1480f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1481f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1482f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1483f721889fSRafael J. Wysocki */ 1484f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1485f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1486f721889fSRafael J. Wysocki { 1487f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1488f721889fSRafael J. Wysocki return; 1489f721889fSRafael J. Wysocki 14905063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 14915063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1492f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1493f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1494f721889fSRafael J. Wysocki genpd->gov = gov; 1495f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1496c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 149717b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1498596ba34bSRafael J. Wysocki genpd->device_count = 0; 1499221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 15006ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1501f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1502f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1503596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1504596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 15050496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 1506596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1507596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 15080496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 1509596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1510596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 15110496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 1512596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1513596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 15140496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 1515596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1516d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 15170496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 1518d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1519596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 15200496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 1521d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 1522596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1523ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 1524ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 1525c11f6f5bSUlf Hansson 1526c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1527c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1528c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1529c11f6f5bSUlf Hansson } 1530c11f6f5bSUlf Hansson 1531fc5cbf0cSAxel Haslam if (genpd->state_idx >= GENPD_MAX_NUM_STATES) { 1532fc5cbf0cSAxel Haslam pr_warn("Initial state index out of bounds.\n"); 1533fc5cbf0cSAxel Haslam genpd->state_idx = GENPD_MAX_NUM_STATES - 1; 1534fc5cbf0cSAxel Haslam } 1535fc5cbf0cSAxel Haslam 1536fc5cbf0cSAxel Haslam if (genpd->state_count > GENPD_MAX_NUM_STATES) { 1537fc5cbf0cSAxel Haslam pr_warn("Limiting states to %d\n", GENPD_MAX_NUM_STATES); 1538fc5cbf0cSAxel Haslam genpd->state_count = GENPD_MAX_NUM_STATES; 1539fc5cbf0cSAxel Haslam } 1540fc5cbf0cSAxel Haslam 1541fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 154290e63452SAxel Haslam if (genpd->state_count == 0) 1543fc5cbf0cSAxel Haslam genpd->state_count = 1; 1544fc5cbf0cSAxel Haslam 15455125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 15465125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 15475125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 15485125bbf3SRafael J. Wysocki } 1549be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1550aa42240aSTomasz Figa 1551aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1552aa42240aSTomasz Figa /* 1553aa42240aSTomasz Figa * Device Tree based PM domain providers. 1554aa42240aSTomasz Figa * 1555aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1556aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1557aa42240aSTomasz Figa * 1558aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1559aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1560aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1561aa42240aSTomasz Figa * 1562aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1563aa42240aSTomasz Figa * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1564aa42240aSTomasz Figa * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by 1565aa42240aSTomasz Figa * index. 1566aa42240aSTomasz Figa */ 1567aa42240aSTomasz Figa 1568aa42240aSTomasz Figa /** 1569aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1570aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1571aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1572aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1573aa42240aSTomasz Figa * into a PM domain. 1574aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1575aa42240aSTomasz Figa */ 1576aa42240aSTomasz Figa struct of_genpd_provider { 1577aa42240aSTomasz Figa struct list_head link; 1578aa42240aSTomasz Figa struct device_node *node; 1579aa42240aSTomasz Figa genpd_xlate_t xlate; 1580aa42240aSTomasz Figa void *data; 1581aa42240aSTomasz Figa }; 1582aa42240aSTomasz Figa 1583aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1584aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1585aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1586aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1587aa42240aSTomasz Figa 1588aa42240aSTomasz Figa /** 1589aa42240aSTomasz Figa * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping 1590aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1591aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1592aa42240aSTomasz Figa * 1593aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1594aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1595aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1596aa42240aSTomasz Figa */ 1597aa42240aSTomasz Figa struct generic_pm_domain *__of_genpd_xlate_simple( 1598aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1599aa42240aSTomasz Figa void *data) 1600aa42240aSTomasz Figa { 1601aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1602aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1603aa42240aSTomasz Figa return data; 1604aa42240aSTomasz Figa } 1605aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple); 1606aa42240aSTomasz Figa 1607aa42240aSTomasz Figa /** 1608aa42240aSTomasz Figa * __of_genpd_xlate_onecell() - Xlate function using a single index. 1609aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1610aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1611aa42240aSTomasz Figa * 1612aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1613aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1614aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1615aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1616aa42240aSTomasz Figa */ 1617aa42240aSTomasz Figa struct generic_pm_domain *__of_genpd_xlate_onecell( 1618aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1619aa42240aSTomasz Figa void *data) 1620aa42240aSTomasz Figa { 1621aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1622aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1623aa42240aSTomasz Figa 1624aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1625aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1626aa42240aSTomasz Figa 1627aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1628aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1629aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1630aa42240aSTomasz Figa } 1631aa42240aSTomasz Figa 1632aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1633aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1634aa42240aSTomasz Figa 1635aa42240aSTomasz Figa return genpd_data->domains[idx]; 1636aa42240aSTomasz Figa } 1637aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell); 1638aa42240aSTomasz Figa 1639aa42240aSTomasz Figa /** 1640aa42240aSTomasz Figa * __of_genpd_add_provider() - Register a PM domain provider for a node 1641aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1642aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1643aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1644aa42240aSTomasz Figa */ 1645aa42240aSTomasz Figa int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1646aa42240aSTomasz Figa void *data) 1647aa42240aSTomasz Figa { 1648aa42240aSTomasz Figa struct of_genpd_provider *cp; 1649aa42240aSTomasz Figa 1650aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1651aa42240aSTomasz Figa if (!cp) 1652aa42240aSTomasz Figa return -ENOMEM; 1653aa42240aSTomasz Figa 1654aa42240aSTomasz Figa cp->node = of_node_get(np); 1655aa42240aSTomasz Figa cp->data = data; 1656aa42240aSTomasz Figa cp->xlate = xlate; 1657aa42240aSTomasz Figa 1658aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1659aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1660aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1661aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1662aa42240aSTomasz Figa 1663aa42240aSTomasz Figa return 0; 1664aa42240aSTomasz Figa } 1665aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_add_provider); 1666aa42240aSTomasz Figa 1667aa42240aSTomasz Figa /** 1668aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1669aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1670aa42240aSTomasz Figa */ 1671aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1672aa42240aSTomasz Figa { 1673aa42240aSTomasz Figa struct of_genpd_provider *cp; 1674aa42240aSTomasz Figa 1675aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1676aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1677aa42240aSTomasz Figa if (cp->node == np) { 1678aa42240aSTomasz Figa list_del(&cp->link); 1679aa42240aSTomasz Figa of_node_put(cp->node); 1680aa42240aSTomasz Figa kfree(cp); 1681aa42240aSTomasz Figa break; 1682aa42240aSTomasz Figa } 1683aa42240aSTomasz Figa } 1684aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1685aa42240aSTomasz Figa } 1686aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1687aa42240aSTomasz Figa 1688aa42240aSTomasz Figa /** 1689aa42240aSTomasz Figa * of_genpd_get_from_provider() - Look-up PM domain 1690aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1691aa42240aSTomasz Figa * 1692aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1693aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1694aa42240aSTomasz Figa * domain. 1695aa42240aSTomasz Figa * 1696aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1697aa42240aSTomasz Figa * on failure. 1698aa42240aSTomasz Figa */ 16997496fcbeSAmit Daniel Kachhap struct generic_pm_domain *of_genpd_get_from_provider( 1700aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1701aa42240aSTomasz Figa { 1702aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1703aa42240aSTomasz Figa struct of_genpd_provider *provider; 1704aa42240aSTomasz Figa 170541795a8aSJon Hunter if (!genpdspec) 170641795a8aSJon Hunter return ERR_PTR(-EINVAL); 170741795a8aSJon Hunter 1708aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1709aa42240aSTomasz Figa 1710aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1711aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1712aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1713aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1714aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1715aa42240aSTomasz Figa break; 1716aa42240aSTomasz Figa } 1717aa42240aSTomasz Figa 1718aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1719aa42240aSTomasz Figa 1720aa42240aSTomasz Figa return genpd; 1721aa42240aSTomasz Figa } 17227496fcbeSAmit Daniel Kachhap EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); 1723aa42240aSTomasz Figa 1724aa42240aSTomasz Figa /** 1725aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 17268bb6944eSJon Hunter * @dev: Device to detach. 1727aa42240aSTomasz Figa * @power_off: Currently not used 1728aa42240aSTomasz Figa * 1729aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1730aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1731aa42240aSTomasz Figa */ 1732aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1733aa42240aSTomasz Figa { 1734446d999cSRussell King struct generic_pm_domain *pd; 173593af5e93SGeert Uytterhoeven unsigned int i; 1736aa42240aSTomasz Figa int ret = 0; 1737aa42240aSTomasz Figa 1738446d999cSRussell King pd = pm_genpd_lookup_dev(dev); 1739aa42240aSTomasz Figa if (!pd) 1740aa42240aSTomasz Figa return; 1741aa42240aSTomasz Figa 1742aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1743aa42240aSTomasz Figa 174493af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1745aa42240aSTomasz Figa ret = pm_genpd_remove_device(pd, dev); 1746aa42240aSTomasz Figa if (ret != -EAGAIN) 1747aa42240aSTomasz Figa break; 174893af5e93SGeert Uytterhoeven 174993af5e93SGeert Uytterhoeven mdelay(i); 1750aa42240aSTomasz Figa cond_resched(); 1751aa42240aSTomasz Figa } 1752aa42240aSTomasz Figa 1753aa42240aSTomasz Figa if (ret < 0) { 1754aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1755aa42240aSTomasz Figa pd->name, ret); 1756aa42240aSTomasz Figa return; 1757aa42240aSTomasz Figa } 1758aa42240aSTomasz Figa 1759aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1760aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1761aa42240aSTomasz Figa } 1762aa42240aSTomasz Figa 1763632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1764632f7ce3SRussell King { 1765632f7ce3SRussell King struct generic_pm_domain *pd; 1766632f7ce3SRussell King 1767632f7ce3SRussell King pd = dev_to_genpd(dev); 1768632f7ce3SRussell King if (IS_ERR(pd)) 1769632f7ce3SRussell King return; 1770632f7ce3SRussell King 1771632f7ce3SRussell King genpd_queue_power_off_work(pd); 1772632f7ce3SRussell King } 1773632f7ce3SRussell King 1774aa42240aSTomasz Figa /** 1775aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1776aa42240aSTomasz Figa * @dev: Device to attach. 1777aa42240aSTomasz Figa * 1778aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1779aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1780aa42240aSTomasz Figa * 1781aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1782aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1783aa42240aSTomasz Figa * 1784311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1785311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1786311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1787311fa6adSJon Hunter * probed and to re-try again later. 1788aa42240aSTomasz Figa */ 1789aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 1790aa42240aSTomasz Figa { 1791aa42240aSTomasz Figa struct of_phandle_args pd_args; 1792aa42240aSTomasz Figa struct generic_pm_domain *pd; 179393af5e93SGeert Uytterhoeven unsigned int i; 1794aa42240aSTomasz Figa int ret; 1795aa42240aSTomasz Figa 1796aa42240aSTomasz Figa if (!dev->of_node) 1797aa42240aSTomasz Figa return -ENODEV; 1798aa42240aSTomasz Figa 1799aa42240aSTomasz Figa if (dev->pm_domain) 1800aa42240aSTomasz Figa return -EEXIST; 1801aa42240aSTomasz Figa 1802aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 1803aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 1804aa42240aSTomasz Figa if (ret < 0) { 1805aa42240aSTomasz Figa if (ret != -ENOENT) 1806aa42240aSTomasz Figa return ret; 1807aa42240aSTomasz Figa 1808aa42240aSTomasz Figa /* 1809aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 1810aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 1811aa42240aSTomasz Figa */ 1812aa42240aSTomasz Figa pd_args.args_count = 0; 1813aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 1814aa42240aSTomasz Figa "samsung,power-domain", 0); 1815aa42240aSTomasz Figa if (!pd_args.np) 1816aa42240aSTomasz Figa return -ENOENT; 1817aa42240aSTomasz Figa } 1818aa42240aSTomasz Figa 1819aa42240aSTomasz Figa pd = of_genpd_get_from_provider(&pd_args); 1820265e2cf6SEric Anholt of_node_put(pd_args.np); 1821aa42240aSTomasz Figa if (IS_ERR(pd)) { 1822aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 1823aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 1824311fa6adSJon Hunter return -EPROBE_DEFER; 1825aa42240aSTomasz Figa } 1826aa42240aSTomasz Figa 1827aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 1828aa42240aSTomasz Figa 182993af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1830aa42240aSTomasz Figa ret = pm_genpd_add_device(pd, dev); 1831aa42240aSTomasz Figa if (ret != -EAGAIN) 1832aa42240aSTomasz Figa break; 183393af5e93SGeert Uytterhoeven 183493af5e93SGeert Uytterhoeven mdelay(i); 1835aa42240aSTomasz Figa cond_resched(); 1836aa42240aSTomasz Figa } 1837aa42240aSTomasz Figa 1838aa42240aSTomasz Figa if (ret < 0) { 1839aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 1840aa42240aSTomasz Figa pd->name, ret); 1841311fa6adSJon Hunter goto out; 1842aa42240aSTomasz Figa } 1843aa42240aSTomasz Figa 1844aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 1845632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 1846aa42240aSTomasz Figa 184753af16f7SUlf Hansson mutex_lock(&pd->lock); 184853af16f7SUlf Hansson ret = genpd_poweron(pd, 0); 184953af16f7SUlf Hansson mutex_unlock(&pd->lock); 1850311fa6adSJon Hunter out: 1851311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 1852aa42240aSTomasz Figa } 1853aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 1854d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 18552bd5306aSMaciej Matraszek 18562bd5306aSMaciej Matraszek 18572bd5306aSMaciej Matraszek /*** debugfs support ***/ 18582bd5306aSMaciej Matraszek 18592bd5306aSMaciej Matraszek #ifdef CONFIG_PM_ADVANCED_DEBUG 18602bd5306aSMaciej Matraszek #include <linux/pm.h> 18612bd5306aSMaciej Matraszek #include <linux/device.h> 18622bd5306aSMaciej Matraszek #include <linux/debugfs.h> 18632bd5306aSMaciej Matraszek #include <linux/seq_file.h> 18642bd5306aSMaciej Matraszek #include <linux/init.h> 18652bd5306aSMaciej Matraszek #include <linux/kobject.h> 18662bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 18672bd5306aSMaciej Matraszek 18682bd5306aSMaciej Matraszek /* 18692bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 1870d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 18712bd5306aSMaciej Matraszek */ 18722bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 18732bd5306aSMaciej Matraszek { 18742bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 18752bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 18762bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 18772bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 18782bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 18792bd5306aSMaciej Matraszek }; 18802bd5306aSMaciej Matraszek const char *p = ""; 18812bd5306aSMaciej Matraszek 18822bd5306aSMaciej Matraszek if (dev->power.runtime_error) 18832bd5306aSMaciej Matraszek p = "error"; 18842bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 18852bd5306aSMaciej Matraszek p = "unsupported"; 18862bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 18872bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 18882bd5306aSMaciej Matraszek else 18892bd5306aSMaciej Matraszek WARN_ON(1); 18902bd5306aSMaciej Matraszek 18912bd5306aSMaciej Matraszek seq_puts(s, p); 18922bd5306aSMaciej Matraszek } 18932bd5306aSMaciej Matraszek 18942bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 189566a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 18962bd5306aSMaciej Matraszek { 18972bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 18982bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 18992bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 19002bd5306aSMaciej Matraszek }; 19012bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 19022bd5306aSMaciej Matraszek const char *kobj_path; 19032bd5306aSMaciej Matraszek struct gpd_link *link; 19046954d432SGeert Uytterhoeven char state[16]; 19052bd5306aSMaciej Matraszek int ret; 19062bd5306aSMaciej Matraszek 190766a5ca4bSKevin Hilman ret = mutex_lock_interruptible(&genpd->lock); 19082bd5306aSMaciej Matraszek if (ret) 19092bd5306aSMaciej Matraszek return -ERESTARTSYS; 19102bd5306aSMaciej Matraszek 191166a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 19122bd5306aSMaciej Matraszek goto exit; 1913fc5cbf0cSAxel Haslam if (genpd->status == GPD_STATE_POWER_OFF) 19140ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 19156954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 1916fc5cbf0cSAxel Haslam else 19176954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 19186954d432SGeert Uytterhoeven status_lookup[genpd->status]); 19196954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 19202bd5306aSMaciej Matraszek 19212bd5306aSMaciej Matraszek /* 19222bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 19232bd5306aSMaciej Matraszek * master and slave, so we are safe. 192466a5ca4bSKevin Hilman * Also genpd->name is immutable. 19252bd5306aSMaciej Matraszek */ 192666a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 19272bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 192866a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 19292bd5306aSMaciej Matraszek seq_puts(s, ", "); 19302bd5306aSMaciej Matraszek } 19312bd5306aSMaciej Matraszek 193266a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 19332bd5306aSMaciej Matraszek kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL); 19342bd5306aSMaciej Matraszek if (kobj_path == NULL) 19352bd5306aSMaciej Matraszek continue; 19362bd5306aSMaciej Matraszek 19372bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 19382bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 19392bd5306aSMaciej Matraszek kfree(kobj_path); 19402bd5306aSMaciej Matraszek } 19412bd5306aSMaciej Matraszek 19422bd5306aSMaciej Matraszek seq_puts(s, "\n"); 19432bd5306aSMaciej Matraszek exit: 194466a5ca4bSKevin Hilman mutex_unlock(&genpd->lock); 19452bd5306aSMaciej Matraszek 19462bd5306aSMaciej Matraszek return 0; 19472bd5306aSMaciej Matraszek } 19482bd5306aSMaciej Matraszek 19492bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 19502bd5306aSMaciej Matraszek { 195166a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 19522bd5306aSMaciej Matraszek int ret = 0; 19532bd5306aSMaciej Matraszek 19542bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 19552bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 19562bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 19572bd5306aSMaciej Matraszek 19582bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 19592bd5306aSMaciej Matraszek if (ret) 19602bd5306aSMaciej Matraszek return -ERESTARTSYS; 19612bd5306aSMaciej Matraszek 196266a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 196366a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 19642bd5306aSMaciej Matraszek if (ret) 19652bd5306aSMaciej Matraszek break; 19662bd5306aSMaciej Matraszek } 19672bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 19682bd5306aSMaciej Matraszek 19692bd5306aSMaciej Matraszek return ret; 19702bd5306aSMaciej Matraszek } 19712bd5306aSMaciej Matraszek 19722bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 19732bd5306aSMaciej Matraszek { 19742bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 19752bd5306aSMaciej Matraszek } 19762bd5306aSMaciej Matraszek 19772bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 19782bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 19792bd5306aSMaciej Matraszek .read = seq_read, 19802bd5306aSMaciej Matraszek .llseek = seq_lseek, 19812bd5306aSMaciej Matraszek .release = single_release, 19822bd5306aSMaciej Matraszek }; 19832bd5306aSMaciej Matraszek 19842bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 19852bd5306aSMaciej Matraszek { 19862bd5306aSMaciej Matraszek struct dentry *d; 19872bd5306aSMaciej Matraszek 19882bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 19892bd5306aSMaciej Matraszek 19902bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 19912bd5306aSMaciej Matraszek return -ENOMEM; 19922bd5306aSMaciej Matraszek 19932bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 19942bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 19952bd5306aSMaciej Matraszek if (!d) 19962bd5306aSMaciej Matraszek return -ENOMEM; 19972bd5306aSMaciej Matraszek 19982bd5306aSMaciej Matraszek return 0; 19992bd5306aSMaciej Matraszek } 20002bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 20012bd5306aSMaciej Matraszek 20022bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 20032bd5306aSMaciej Matraszek { 20042bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 20052bd5306aSMaciej Matraszek } 20062bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 20072bd5306aSMaciej Matraszek #endif /* CONFIG_PM_ADVANCED_DEBUG */ 2008