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 4902b1d88cdSUlf Hansson genpd_start_dev(genpd, dev); 4912b1d88cdSUlf Hansson genpd_restore_dev(genpd, dev); 4922b1d88cdSUlf Hansson 4932b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 494ffe12855SUlf Hansson if (timed && runtime_pm) { 4952b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 4962b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 4972b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 4982b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 4992b1d88cdSUlf Hansson elapsed_ns); 5002b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5012b1d88cdSUlf Hansson td->constraint_changed = true; 5022b1d88cdSUlf Hansson } 5032b1d88cdSUlf Hansson } 50417b75ecaSRafael J. Wysocki 505f721889fSRafael J. Wysocki return 0; 506f721889fSRafael J. Wysocki } 507f721889fSRafael J. Wysocki 50839ac5ba5STushar Behera static bool pd_ignore_unused; 50939ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 51039ac5ba5STushar Behera { 51139ac5ba5STushar Behera pd_ignore_unused = true; 51239ac5ba5STushar Behera return 1; 51339ac5ba5STushar Behera } 51439ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 51539ac5ba5STushar Behera 51617f2ae7fSRafael J. Wysocki /** 517bb4b72fcSUlf Hansson * genpd_poweroff_unused - Power off all PM domains with no devices in use. 51817f2ae7fSRafael J. Wysocki */ 519bb4b72fcSUlf Hansson static int __init genpd_poweroff_unused(void) 52017f2ae7fSRafael J. Wysocki { 52117f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 52217f2ae7fSRafael J. Wysocki 52339ac5ba5STushar Behera if (pd_ignore_unused) { 52439ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 525bb4b72fcSUlf Hansson return 0; 52639ac5ba5STushar Behera } 52739ac5ba5STushar Behera 52817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 52917f2ae7fSRafael J. Wysocki 53017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 53117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 53217f2ae7fSRafael J. Wysocki 53317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 53417f2ae7fSRafael J. Wysocki 5352fe71dcdSUlf Hansson return 0; 5362fe71dcdSUlf Hansson } 5372fe71dcdSUlf Hansson late_initcall(genpd_poweroff_unused); 5382fe71dcdSUlf Hansson 539596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 540596ba34bSRafael J. Wysocki 54177f827deSRafael J. Wysocki /** 54277f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 54377f827deSRafael J. Wysocki * @genpd: PM domain to check. 54477f827deSRafael J. Wysocki */ 545895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 54677f827deSRafael J. Wysocki { 547895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 54877f827deSRafael J. Wysocki 54977f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 55077f827deSRafael J. Wysocki return false; 55177f827deSRafael J. Wysocki 55277f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 55377f827deSRafael J. Wysocki if (gpd == genpd) 55477f827deSRafael J. Wysocki return true; 55577f827deSRafael J. Wysocki 55677f827deSRafael J. Wysocki return false; 55777f827deSRafael J. Wysocki } 55877f827deSRafael J. Wysocki 559d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 560d5e4cbfeSRafael J. Wysocki struct device *dev) 561d5e4cbfeSRafael J. Wysocki { 562d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 563d5e4cbfeSRafael J. Wysocki } 564d5e4cbfeSRafael J. Wysocki 565596ba34bSRafael J. Wysocki /** 5665063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 567596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 568a4630c61SGeert Uytterhoeven * @timed: True if latency measurements are allowed. 569596ba34bSRafael J. Wysocki * 570596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 5715063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 572596ba34bSRafael J. Wysocki * 57377f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 57477f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 57577f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 57677f827deSRafael J. Wysocki * parallel). 577596ba34bSRafael J. Wysocki */ 578a4630c61SGeert Uytterhoeven static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, 579a4630c61SGeert Uytterhoeven bool timed) 580596ba34bSRafael J. Wysocki { 5815063ce15SRafael J. Wysocki struct gpd_link *link; 582596ba34bSRafael J. Wysocki 58317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 584596ba34bSRafael J. Wysocki return; 585596ba34bSRafael J. Wysocki 586c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 587c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 588596ba34bSRafael J. Wysocki return; 589596ba34bSRafael J. Wysocki 590fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 591fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 592a4630c61SGeert Uytterhoeven genpd_power_off(genpd, timed); 593596ba34bSRafael J. Wysocki 59417b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 5955063ce15SRafael J. Wysocki 5965063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5975063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 598a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(link->master, timed); 599596ba34bSRafael J. Wysocki } 600596ba34bSRafael J. Wysocki } 601596ba34bSRafael J. Wysocki 602596ba34bSRafael J. Wysocki /** 603802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 604802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 605a4630c61SGeert Uytterhoeven * @timed: True if latency measurements are allowed. 606802d8b49SRafael J. Wysocki * 60777f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 60877f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 60977f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 61077f827deSRafael J. Wysocki * parallel). 611802d8b49SRafael J. Wysocki */ 612a4630c61SGeert Uytterhoeven static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd, 613a4630c61SGeert Uytterhoeven bool timed) 614802d8b49SRafael J. Wysocki { 615802d8b49SRafael J. Wysocki struct gpd_link *link; 616802d8b49SRafael J. Wysocki 617ba2bbfbfSUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 618802d8b49SRafael J. Wysocki return; 619802d8b49SRafael J. Wysocki 620802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 621a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(link->master, timed); 622802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 623802d8b49SRafael J. Wysocki } 624802d8b49SRafael J. Wysocki 625a4630c61SGeert Uytterhoeven genpd_power_on(genpd, timed); 626802d8b49SRafael J. Wysocki 627802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 628802d8b49SRafael J. Wysocki } 629802d8b49SRafael J. Wysocki 630802d8b49SRafael J. Wysocki /** 6314ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 6324ecd6e65SRafael J. Wysocki * @dev: Device to check. 6334ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 6344ecd6e65SRafael J. Wysocki * 6354ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 6364ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 6374ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 6384ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 6394ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 6404ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 6414ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 6424ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 6434ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 6444ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 6454ecd6e65SRafael J. Wysocki */ 6464ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 6474ecd6e65SRafael J. Wysocki { 6484ecd6e65SRafael J. Wysocki bool active_wakeup; 6494ecd6e65SRafael J. Wysocki 6504ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 6514ecd6e65SRafael J. Wysocki return false; 6524ecd6e65SRafael J. Wysocki 653d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 6544ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 6554ecd6e65SRafael J. Wysocki } 6564ecd6e65SRafael J. Wysocki 6574ecd6e65SRafael J. Wysocki /** 658596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 659596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 660596ba34bSRafael J. Wysocki * 661596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 662596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 663596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 664596ba34bSRafael J. Wysocki * consisting of I/O devices. 665596ba34bSRafael J. Wysocki */ 666596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 667596ba34bSRafael J. Wysocki { 668596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 669b6c10c84SRafael J. Wysocki int ret; 670596ba34bSRafael J. Wysocki 671596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 672596ba34bSRafael J. Wysocki 673596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 674596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 675596ba34bSRafael J. Wysocki return -EINVAL; 676596ba34bSRafael J. Wysocki 67717b75ecaSRafael J. Wysocki /* 67817b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 67917b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 68017b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 68117b75ecaSRafael J. Wysocki */ 68217b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 68317b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 68417b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 68517b75ecaSRafael J. Wysocki 68617b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 68784167035SUlf Hansson pm_runtime_put(dev); 68817b75ecaSRafael J. Wysocki return -EBUSY; 68917b75ecaSRafael J. Wysocki } 69017b75ecaSRafael J. Wysocki 6914ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 6924ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 6934ecd6e65SRafael J. Wysocki 694ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 695596ba34bSRafael J. Wysocki 69665533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 69765533bbfSRafael J. Wysocki genpd->suspended_count = 0; 69817b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 69965533bbfSRafael J. Wysocki } 70017b75ecaSRafael J. Wysocki 701ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 702596ba34bSRafael J. Wysocki 703596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 70417b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 705596ba34bSRafael J. Wysocki return 0; 706596ba34bSRafael J. Wysocki } 707596ba34bSRafael J. Wysocki 708596ba34bSRafael J. Wysocki /* 70917b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 7107420aa4fSUlf Hansson * so genpd_poweron() will return immediately, but if the device 711d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 71217b75ecaSRafael J. Wysocki * to make it operational. 713596ba34bSRafael J. Wysocki */ 71417b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 715596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 716596ba34bSRafael J. Wysocki 717b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 718b6c10c84SRafael J. Wysocki if (ret) { 719b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 720b6c10c84SRafael J. Wysocki 721b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 722b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 723b6c10c84SRafael J. Wysocki 724b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 72517b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 726b6c10c84SRafael J. Wysocki } 72717b75ecaSRafael J. Wysocki 72884167035SUlf Hansson pm_runtime_put(dev); 729b6c10c84SRafael J. Wysocki return ret; 730596ba34bSRafael J. Wysocki } 731596ba34bSRafael J. Wysocki 732596ba34bSRafael J. Wysocki /** 733596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 734596ba34bSRafael J. Wysocki * @dev: Device to suspend. 735596ba34bSRafael J. Wysocki * 736596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 737596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 738596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 739596ba34bSRafael J. Wysocki */ 740596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 741596ba34bSRafael J. Wysocki { 742596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 743596ba34bSRafael J. Wysocki 744596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 745596ba34bSRafael J. Wysocki 746596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 747596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 748596ba34bSRafael J. Wysocki return -EINVAL; 749596ba34bSRafael J. Wysocki 7501e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 751596ba34bSRafael J. Wysocki } 752596ba34bSRafael J. Wysocki 753596ba34bSRafael J. Wysocki /** 7540496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 755596ba34bSRafael J. Wysocki * @dev: Device to suspend. 756596ba34bSRafael J. Wysocki * 757596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 758596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 759596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 760596ba34bSRafael J. Wysocki */ 7610496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 762596ba34bSRafael J. Wysocki { 763596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 764596ba34bSRafael J. Wysocki 765596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 766596ba34bSRafael J. Wysocki 767596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 768596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 769596ba34bSRafael J. Wysocki return -EINVAL; 770596ba34bSRafael J. Wysocki 7711e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_suspend_late(dev); 7720496c8aeSRafael J. Wysocki } 773596ba34bSRafael J. Wysocki 7740496c8aeSRafael J. Wysocki /** 7750496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 7760496c8aeSRafael J. Wysocki * @dev: Device to suspend. 7770496c8aeSRafael J. Wysocki * 7780496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 7790496c8aeSRafael J. Wysocki * been stopped. 7800496c8aeSRafael J. Wysocki */ 7810496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 7820496c8aeSRafael J. Wysocki { 7830496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 784596ba34bSRafael J. Wysocki 7850496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 7860496c8aeSRafael J. Wysocki 7870496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 7880496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 7890496c8aeSRafael J. Wysocki return -EINVAL; 7900496c8aeSRafael J. Wysocki 791dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 7920496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 793d4f2d87aSRafael J. Wysocki return 0; 794d4f2d87aSRafael J. Wysocki 7952b1d88cdSUlf Hansson genpd_stop_dev(genpd, dev); 796596ba34bSRafael J. Wysocki 797596ba34bSRafael J. Wysocki /* 798596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 799596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 800596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 801596ba34bSRafael J. Wysocki */ 802596ba34bSRafael J. Wysocki genpd->suspended_count++; 803a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(genpd, true); 804596ba34bSRafael J. Wysocki 805596ba34bSRafael J. Wysocki return 0; 806596ba34bSRafael J. Wysocki } 807596ba34bSRafael J. Wysocki 808596ba34bSRafael J. Wysocki /** 8090496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 810596ba34bSRafael J. Wysocki * @dev: Device to resume. 811596ba34bSRafael J. Wysocki * 8120496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 813596ba34bSRafael J. Wysocki */ 814596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 815596ba34bSRafael J. Wysocki { 816596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 817596ba34bSRafael J. Wysocki 818596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 819596ba34bSRafael J. Wysocki 820596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 821596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 822596ba34bSRafael J. Wysocki return -EINVAL; 823596ba34bSRafael J. Wysocki 824dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 825cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 826596ba34bSRafael J. Wysocki return 0; 827596ba34bSRafael J. Wysocki 828596ba34bSRafael J. Wysocki /* 829596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 830596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 831596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 832596ba34bSRafael J. Wysocki */ 833a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, true); 834596ba34bSRafael J. Wysocki genpd->suspended_count--; 835596ba34bSRafael J. Wysocki 8362b1d88cdSUlf Hansson return genpd_start_dev(genpd, dev); 837596ba34bSRafael J. Wysocki } 838596ba34bSRafael J. Wysocki 839596ba34bSRafael J. Wysocki /** 8400496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 8410496c8aeSRafael J. Wysocki * @dev: Device to resume. 8420496c8aeSRafael J. Wysocki * 8430496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 8440496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 8450496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 8460496c8aeSRafael J. Wysocki * devices. 8470496c8aeSRafael J. Wysocki */ 8480496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 8490496c8aeSRafael J. Wysocki { 8500496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 8510496c8aeSRafael J. Wysocki 8520496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8530496c8aeSRafael J. Wysocki 8540496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 8550496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 8560496c8aeSRafael J. Wysocki return -EINVAL; 8570496c8aeSRafael J. Wysocki 8581e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_resume_early(dev); 8590496c8aeSRafael J. Wysocki } 8600496c8aeSRafael J. Wysocki 8610496c8aeSRafael J. Wysocki /** 8620496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 863596ba34bSRafael J. Wysocki * @dev: Device to resume. 864596ba34bSRafael J. Wysocki * 865596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 866596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 867596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 868596ba34bSRafael J. Wysocki */ 869596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 870596ba34bSRafael J. Wysocki { 871596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 872596ba34bSRafael J. Wysocki 873596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 874596ba34bSRafael J. Wysocki 875596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 876596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 877596ba34bSRafael J. Wysocki return -EINVAL; 878596ba34bSRafael J. Wysocki 8791e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 880596ba34bSRafael J. Wysocki } 881596ba34bSRafael J. Wysocki 882596ba34bSRafael J. Wysocki /** 8830496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 884596ba34bSRafael J. Wysocki * @dev: Device to freeze. 885596ba34bSRafael J. Wysocki * 886596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 887596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 888596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 889596ba34bSRafael J. Wysocki */ 890596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 891596ba34bSRafael J. Wysocki { 892596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 893596ba34bSRafael J. Wysocki 894596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 895596ba34bSRafael J. Wysocki 896596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 897596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 898596ba34bSRafael J. Wysocki return -EINVAL; 899596ba34bSRafael J. Wysocki 9001e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 901596ba34bSRafael J. Wysocki } 902596ba34bSRafael J. Wysocki 903596ba34bSRafael J. Wysocki /** 9040496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 9050496c8aeSRafael J. Wysocki * @dev: Device to freeze. 9060496c8aeSRafael J. Wysocki * 9070496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 9080496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9090496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9100496c8aeSRafael J. Wysocki * devices. 9110496c8aeSRafael J. Wysocki */ 9120496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 9130496c8aeSRafael J. Wysocki { 9140496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 9150496c8aeSRafael J. Wysocki 9160496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9170496c8aeSRafael J. Wysocki 9180496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9190496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9200496c8aeSRafael J. Wysocki return -EINVAL; 9210496c8aeSRafael J. Wysocki 9221e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_freeze_late(dev); 9230496c8aeSRafael J. Wysocki } 9240496c8aeSRafael J. Wysocki 9250496c8aeSRafael J. Wysocki /** 9260496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 927596ba34bSRafael J. Wysocki * @dev: Device to freeze. 928596ba34bSRafael J. Wysocki * 929596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 930596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 931596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 932596ba34bSRafael J. Wysocki * devices. 933596ba34bSRafael J. Wysocki */ 934596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 935596ba34bSRafael J. Wysocki { 936596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 937596ba34bSRafael J. Wysocki 938596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 939596ba34bSRafael J. Wysocki 940596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 941596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 942596ba34bSRafael J. Wysocki return -EINVAL; 943596ba34bSRafael J. Wysocki 9442b1d88cdSUlf Hansson return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); 945596ba34bSRafael J. Wysocki } 946596ba34bSRafael J. Wysocki 947596ba34bSRafael J. Wysocki /** 9480496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 949596ba34bSRafael J. Wysocki * @dev: Device to thaw. 950596ba34bSRafael J. Wysocki * 9510496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 9520496c8aeSRafael J. Wysocki * before the system transition. 953596ba34bSRafael J. Wysocki */ 954596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 955596ba34bSRafael J. Wysocki { 956596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 957596ba34bSRafael J. Wysocki 958596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 959596ba34bSRafael J. Wysocki 960596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 961596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 962596ba34bSRafael J. Wysocki return -EINVAL; 963596ba34bSRafael J. Wysocki 96451cda844SUlf Hansson return genpd->suspend_power_off ? 9652b1d88cdSUlf Hansson 0 : genpd_start_dev(genpd, dev); 9660496c8aeSRafael J. Wysocki } 967596ba34bSRafael J. Wysocki 9680496c8aeSRafael J. Wysocki /** 9690496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 9700496c8aeSRafael J. Wysocki * @dev: Device to thaw. 9710496c8aeSRafael J. Wysocki * 9720496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 9730496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9740496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9750496c8aeSRafael J. Wysocki * devices. 9760496c8aeSRafael J. Wysocki */ 9770496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 9780496c8aeSRafael J. Wysocki { 9790496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 980596ba34bSRafael J. Wysocki 9810496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9820496c8aeSRafael J. Wysocki 9830496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9840496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9850496c8aeSRafael J. Wysocki return -EINVAL; 9860496c8aeSRafael J. Wysocki 9871e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_thaw_early(dev); 988596ba34bSRafael J. Wysocki } 989596ba34bSRafael J. Wysocki 990596ba34bSRafael J. Wysocki /** 991596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 992596ba34bSRafael J. Wysocki * @dev: Device to thaw. 993596ba34bSRafael J. Wysocki * 994596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 995596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 996596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 997596ba34bSRafael J. Wysocki */ 998596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 999596ba34bSRafael J. Wysocki { 1000596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1001596ba34bSRafael J. Wysocki 1002596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1003596ba34bSRafael J. Wysocki 1004596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1005596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1006596ba34bSRafael J. Wysocki return -EINVAL; 1007596ba34bSRafael J. Wysocki 10081e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 1009596ba34bSRafael J. Wysocki } 1010596ba34bSRafael J. Wysocki 1011596ba34bSRafael J. Wysocki /** 10120496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1013596ba34bSRafael J. Wysocki * @dev: Device to resume. 1014596ba34bSRafael J. Wysocki * 10150496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 10160496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1017596ba34bSRafael J. Wysocki */ 1018596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1019596ba34bSRafael J. Wysocki { 1020596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1021596ba34bSRafael J. Wysocki 1022596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1023596ba34bSRafael J. Wysocki 1024596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1025596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1026596ba34bSRafael J. Wysocki return -EINVAL; 1027596ba34bSRafael J. Wysocki 1028596ba34bSRafael J. Wysocki /* 1029596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1030596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1031596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 103265533bbfSRafael J. Wysocki * 103365533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 103465533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 103565533bbfSRafael J. Wysocki */ 103665533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 103765533bbfSRafael J. Wysocki /* 103865533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1039802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 1040802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1041596ba34bSRafael J. Wysocki */ 104217b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1043596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1044596ba34bSRafael J. Wysocki /* 104565533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 104665533bbfSRafael J. Wysocki * sure it will be off going forward. 1047596ba34bSRafael J. Wysocki */ 1048a4630c61SGeert Uytterhoeven genpd_power_off(genpd, true); 104965533bbfSRafael J. Wysocki 1050596ba34bSRafael J. Wysocki return 0; 1051596ba34bSRafael J. Wysocki } 105265533bbfSRafael J. Wysocki } 1053596ba34bSRafael J. Wysocki 105418dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 105518dd2eceSRafael J. Wysocki return 0; 105618dd2eceSRafael J. Wysocki 1057a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, true); 1058596ba34bSRafael J. Wysocki 10592b1d88cdSUlf Hansson return genpd_start_dev(genpd, dev); 1060596ba34bSRafael J. Wysocki } 1061596ba34bSRafael J. Wysocki 1062596ba34bSRafael J. Wysocki /** 1063596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1064596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1065596ba34bSRafael J. Wysocki * 1066596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1067596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1068596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1069596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1070596ba34bSRafael J. Wysocki */ 1071596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1072596ba34bSRafael J. Wysocki { 1073596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1074596ba34bSRafael J. Wysocki bool run_complete; 1075596ba34bSRafael J. Wysocki 1076596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1077596ba34bSRafael J. Wysocki 1078596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1079596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1080596ba34bSRafael J. Wysocki return; 1081596ba34bSRafael J. Wysocki 1082596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1083596ba34bSRafael J. Wysocki 1084596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1085596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1086596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1087596ba34bSRafael J. Wysocki 1088596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1089596ba34bSRafael J. Wysocki 1090596ba34bSRafael J. Wysocki if (run_complete) { 1091596ba34bSRafael J. Wysocki pm_generic_complete(dev); 10926f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1093596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 1094af939339SUlf Hansson pm_request_idle(dev); 1095596ba34bSRafael J. Wysocki } 1096596ba34bSRafael J. Wysocki } 1097596ba34bSRafael J. Wysocki 109877f827deSRafael J. Wysocki /** 1099d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 110077f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 110177f827deSRafael J. Wysocki * 110277f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 110377f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 110477f827deSRafael J. Wysocki */ 1105d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 110677f827deSRafael J. Wysocki { 110777f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 110877f827deSRafael J. Wysocki 110977f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 111077f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 111177f827deSRafael J. Wysocki return; 111277f827deSRafael J. Wysocki 111377f827deSRafael J. Wysocki if (suspend) { 111477f827deSRafael J. Wysocki genpd->suspended_count++; 1115a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(genpd, false); 111677f827deSRafael J. Wysocki } else { 1117a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, false); 111877f827deSRafael J. Wysocki genpd->suspended_count--; 111977f827deSRafael J. Wysocki } 112077f827deSRafael J. Wysocki } 1121d47e6464SUlf Hansson 1122d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1123d47e6464SUlf Hansson { 1124d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1125d47e6464SUlf Hansson } 1126d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1127d47e6464SUlf Hansson 1128d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1129d47e6464SUlf Hansson { 1130d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1131d47e6464SUlf Hansson } 1132d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 113377f827deSRafael J. Wysocki 1134d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1135596ba34bSRafael J. Wysocki 1136596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1137596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 11380496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1139596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 11400496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1141596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1142596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1143596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 11440496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1145596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 11460496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1147596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1148596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1149596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1150596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1151596ba34bSRafael J. Wysocki 1152596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1153596ba34bSRafael J. Wysocki 1154f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1155f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1156f104e1e5SUlf Hansson struct gpd_timing_data *td) 11571d5fcfecSRafael J. Wysocki { 11581d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11593e235685SUlf Hansson int ret; 11603e235685SUlf Hansson 11613e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11623e235685SUlf Hansson if (ret) 11633e235685SUlf Hansson return ERR_PTR(ret); 11641d5fcfecSRafael J. Wysocki 11651d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 11663e235685SUlf Hansson if (!gpd_data) { 11673e235685SUlf Hansson ret = -ENOMEM; 11683e235685SUlf Hansson goto err_put; 11693e235685SUlf Hansson } 11701d5fcfecSRafael J. Wysocki 1171f104e1e5SUlf Hansson if (td) 1172f104e1e5SUlf Hansson gpd_data->td = *td; 1173f104e1e5SUlf Hansson 1174f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1175f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1176f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1177f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1178f104e1e5SUlf Hansson 1179f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1180f104e1e5SUlf Hansson 1181f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1182f104e1e5SUlf Hansson ret = -EINVAL; 1183f104e1e5SUlf Hansson goto err_free; 1184f104e1e5SUlf Hansson } 1185f104e1e5SUlf Hansson 1186f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1187f104e1e5SUlf Hansson 1188f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1189f104e1e5SUlf Hansson 1190989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1191989561deSTomeu Vizoso 11921d5fcfecSRafael J. Wysocki return gpd_data; 11933e235685SUlf Hansson 1194f104e1e5SUlf Hansson err_free: 1195f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1196f104e1e5SUlf Hansson kfree(gpd_data); 11973e235685SUlf Hansson err_put: 11983e235685SUlf Hansson dev_pm_put_subsys_data(dev); 11993e235685SUlf Hansson return ERR_PTR(ret); 12001d5fcfecSRafael J. Wysocki } 12011d5fcfecSRafael J. Wysocki 120249d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 12031d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 12041d5fcfecSRafael J. Wysocki { 1205989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1206989561deSTomeu Vizoso 1207f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1208f104e1e5SUlf Hansson 1209f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1210f104e1e5SUlf Hansson 1211f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1212f104e1e5SUlf Hansson 12131d5fcfecSRafael J. Wysocki kfree(gpd_data); 12143e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12151d5fcfecSRafael J. Wysocki } 12161d5fcfecSRafael J. Wysocki 1217f721889fSRafael J. Wysocki /** 1218b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1219f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1220f721889fSRafael J. Wysocki * @dev: Device to be added. 1221b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1222f721889fSRafael J. Wysocki */ 1223b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1224b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1225f721889fSRafael J. Wysocki { 1226c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1227f721889fSRafael J. Wysocki int ret = 0; 1228f721889fSRafael J. Wysocki 1229f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1230f721889fSRafael J. Wysocki 1231f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1232f721889fSRafael J. Wysocki return -EINVAL; 1233f721889fSRafael J. Wysocki 1234f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 12353e235685SUlf Hansson if (IS_ERR(gpd_data)) 12363e235685SUlf Hansson return PTR_ERR(gpd_data); 12376ff7bb0dSRafael J. Wysocki 1238ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1239f721889fSRafael J. Wysocki 1240596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1241596ba34bSRafael J. Wysocki ret = -EAGAIN; 1242596ba34bSRafael J. Wysocki goto out; 1243596ba34bSRafael J. Wysocki } 1244596ba34bSRafael J. Wysocki 1245b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1246b472c2faSUlf Hansson if (ret) 1247b472c2faSUlf Hansson goto out; 1248d79b6fe1SGeert Uytterhoeven 124914b53064SUlf Hansson genpd->device_count++; 125014b53064SUlf Hansson genpd->max_off_time_changed = true; 125114b53064SUlf Hansson 12521d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 12536ff7bb0dSRafael J. Wysocki 1254f721889fSRafael J. Wysocki out: 1255ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1256f721889fSRafael J. Wysocki 1257c0356db7SUlf Hansson if (ret) 1258c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1259c0356db7SUlf Hansson else 1260c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12611d5fcfecSRafael J. Wysocki 1262f721889fSRafael J. Wysocki return ret; 1263f721889fSRafael J. Wysocki } 126424c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1265f721889fSRafael J. Wysocki 1266f721889fSRafael J. Wysocki /** 1267f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1268f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1269f721889fSRafael J. Wysocki * @dev: Device to be removed. 1270f721889fSRafael J. Wysocki */ 1271f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1272f721889fSRafael J. Wysocki struct device *dev) 1273f721889fSRafael J. Wysocki { 12746ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12754605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1276efa69025SRafael J. Wysocki int ret = 0; 1277f721889fSRafael J. Wysocki 1278f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1279f721889fSRafael J. Wysocki 1280df6a0d6fSRussell King if (!genpd || genpd != pm_genpd_lookup_dev(dev)) 1281f721889fSRafael J. Wysocki return -EINVAL; 1282f721889fSRafael J. Wysocki 1283c0356db7SUlf Hansson /* The above validation also means we have existing domain_data. */ 1284c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1285c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1286c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1287c0356db7SUlf Hansson 1288ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1289f721889fSRafael J. Wysocki 1290596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1291596ba34bSRafael J. Wysocki ret = -EAGAIN; 1292596ba34bSRafael J. Wysocki goto out; 1293596ba34bSRafael J. Wysocki } 1294596ba34bSRafael J. Wysocki 12956ff7bb0dSRafael J. Wysocki genpd->device_count--; 12966ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 12976ff7bb0dSRafael J. Wysocki 1298d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1299c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1300d79b6fe1SGeert Uytterhoeven 1301efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 13026ff7bb0dSRafael J. Wysocki 1303ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 13046ff7bb0dSRafael J. Wysocki 130549d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 13061d5fcfecSRafael J. Wysocki 13076ff7bb0dSRafael J. Wysocki return 0; 1308f721889fSRafael J. Wysocki 1309596ba34bSRafael J. Wysocki out: 1310ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1311c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1312f721889fSRafael J. Wysocki 1313f721889fSRafael J. Wysocki return ret; 1314f721889fSRafael J. Wysocki } 131524c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1316f721889fSRafael J. Wysocki 1317f721889fSRafael J. Wysocki /** 1318f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1319f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1320bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1321f721889fSRafael J. Wysocki */ 1322f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1323bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1324f721889fSRafael J. Wysocki { 13252547923dSLina Iyer struct gpd_link *link, *itr; 1326f721889fSRafael J. Wysocki int ret = 0; 1327f721889fSRafael J. Wysocki 1328fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1329fb7268beSRafael J. Wysocki || genpd == subdomain) 1330f721889fSRafael J. Wysocki return -EINVAL; 1331f721889fSRafael J. Wysocki 13322547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13332547923dSLina Iyer if (!link) 13342547923dSLina Iyer return -ENOMEM; 13352547923dSLina Iyer 1336cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1337cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1338f721889fSRafael J. Wysocki 133917b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1340bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1341f721889fSRafael J. Wysocki ret = -EINVAL; 1342f721889fSRafael J. Wysocki goto out; 1343f721889fSRafael J. Wysocki } 1344f721889fSRafael J. Wysocki 13452547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 13462547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1347f721889fSRafael J. Wysocki ret = -EINVAL; 1348f721889fSRafael J. Wysocki goto out; 1349f721889fSRafael J. Wysocki } 1350f721889fSRafael J. Wysocki } 1351f721889fSRafael J. Wysocki 13525063ce15SRafael J. Wysocki link->master = genpd; 13535063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1354bc0403ffSRafael J. Wysocki link->slave = subdomain; 1355bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1356bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1357c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1358f721889fSRafael J. Wysocki 1359f721889fSRafael J. Wysocki out: 1360ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1361cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 13622547923dSLina Iyer if (ret) 13632547923dSLina Iyer kfree(link); 1364f721889fSRafael J. Wysocki return ret; 1365f721889fSRafael J. Wysocki } 1366d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1367f721889fSRafael J. Wysocki 1368f721889fSRafael J. Wysocki /** 1369f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1370f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 13715063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1372f721889fSRafael J. Wysocki */ 1373f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 13745063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1375f721889fSRafael J. Wysocki { 13765063ce15SRafael J. Wysocki struct gpd_link *link; 1377f721889fSRafael J. Wysocki int ret = -EINVAL; 1378f721889fSRafael J. Wysocki 13795063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1380f721889fSRafael J. Wysocki return -EINVAL; 1381f721889fSRafael J. Wysocki 1382cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1383cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1384f721889fSRafael J. Wysocki 138530e7a65bSJon Hunter if (!list_empty(&subdomain->slave_links) || subdomain->device_count) { 138630e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 138730e7a65bSJon Hunter subdomain->name); 138830e7a65bSJon Hunter ret = -EBUSY; 138930e7a65bSJon Hunter goto out; 139030e7a65bSJon Hunter } 139130e7a65bSJon Hunter 13925063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 13935063ce15SRafael J. Wysocki if (link->slave != subdomain) 1394f721889fSRafael J. Wysocki continue; 1395f721889fSRafael J. Wysocki 13965063ce15SRafael J. Wysocki list_del(&link->master_node); 13975063ce15SRafael J. Wysocki list_del(&link->slave_node); 13985063ce15SRafael J. Wysocki kfree(link); 139917b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1400f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1401f721889fSRafael J. Wysocki 1402f721889fSRafael J. Wysocki ret = 0; 1403f721889fSRafael J. Wysocki break; 1404f721889fSRafael J. Wysocki } 1405f721889fSRafael J. Wysocki 140630e7a65bSJon Hunter out: 1407ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1408cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 1409f721889fSRafael J. Wysocki 1410f721889fSRafael J. Wysocki return ret; 1411f721889fSRafael J. Wysocki } 1412d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1413f721889fSRafael J. Wysocki 1414d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1415d23b9b00SRafael J. Wysocki 1416d5e4cbfeSRafael J. Wysocki /** 141712e10bb6SGeert Uytterhoeven * pm_genpd_default_save_state - Default "save device state" for PM domains. 1418ecf00475SRafael J. Wysocki * @dev: Device to handle. 1419ecf00475SRafael J. Wysocki */ 1420ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1421ecf00475SRafael J. Wysocki { 1422ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1423ecf00475SRafael J. Wysocki 14240b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 14250b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 14260b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 14270b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 14280b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 14290b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 14300b589741SRafael J. Wysocki else 14310b589741SRafael J. Wysocki cb = NULL; 1432ecf00475SRafael J. Wysocki 14330b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 14340b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 14350b589741SRafael J. Wysocki 14360b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1437ecf00475SRafael J. Wysocki } 1438ecf00475SRafael J. Wysocki 1439ecf00475SRafael J. Wysocki /** 144012e10bb6SGeert Uytterhoeven * pm_genpd_default_restore_state - Default PM domains "restore device state". 1441ecf00475SRafael J. Wysocki * @dev: Device to handle. 1442ecf00475SRafael J. Wysocki */ 1443ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1444ecf00475SRafael J. Wysocki { 1445ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1446ecf00475SRafael J. Wysocki 14470b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 14480b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 14490b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 14500b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 14510b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 14520b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 14530b589741SRafael J. Wysocki else 14540b589741SRafael J. Wysocki cb = NULL; 1455ecf00475SRafael J. Wysocki 14560b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 14570b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 14580b589741SRafael J. Wysocki 14590b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1460ecf00475SRafael J. Wysocki } 1461ecf00475SRafael J. Wysocki 1462d23b9b00SRafael J. Wysocki /** 1463f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1464f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1465f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1466f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1467f721889fSRafael J. Wysocki */ 1468f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1469f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1470f721889fSRafael J. Wysocki { 1471f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1472f721889fSRafael J. Wysocki return; 1473f721889fSRafael J. Wysocki 14745063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 14755063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1476f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1477f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1478f721889fSRafael J. Wysocki genpd->gov = gov; 1479f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1480c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 148117b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1482596ba34bSRafael J. Wysocki genpd->device_count = 0; 1483221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 14846ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1485f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1486f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1487596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1488596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 14890496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 1490596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1491596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 14920496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 1493596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1494596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 14950496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 1496596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1497596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 14980496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 1499596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1500d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 15010496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 1502d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1503596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 15040496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 1505d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 1506596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1507ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 1508ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 1509c11f6f5bSUlf Hansson 1510c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1511c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1512c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1513c11f6f5bSUlf Hansson } 1514c11f6f5bSUlf Hansson 1515fc5cbf0cSAxel Haslam if (genpd->state_idx >= GENPD_MAX_NUM_STATES) { 1516fc5cbf0cSAxel Haslam pr_warn("Initial state index out of bounds.\n"); 1517fc5cbf0cSAxel Haslam genpd->state_idx = GENPD_MAX_NUM_STATES - 1; 1518fc5cbf0cSAxel Haslam } 1519fc5cbf0cSAxel Haslam 1520fc5cbf0cSAxel Haslam if (genpd->state_count > GENPD_MAX_NUM_STATES) { 1521fc5cbf0cSAxel Haslam pr_warn("Limiting states to %d\n", GENPD_MAX_NUM_STATES); 1522fc5cbf0cSAxel Haslam genpd->state_count = GENPD_MAX_NUM_STATES; 1523fc5cbf0cSAxel Haslam } 1524fc5cbf0cSAxel Haslam 1525fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 152690e63452SAxel Haslam if (genpd->state_count == 0) 1527fc5cbf0cSAxel Haslam genpd->state_count = 1; 1528fc5cbf0cSAxel Haslam 15295125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 15305125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 15315125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 15325125bbf3SRafael J. Wysocki } 1533be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1534aa42240aSTomasz Figa 1535aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1536aa42240aSTomasz Figa /* 1537aa42240aSTomasz Figa * Device Tree based PM domain providers. 1538aa42240aSTomasz Figa * 1539aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1540aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1541aa42240aSTomasz Figa * 1542aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1543aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1544aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1545aa42240aSTomasz Figa * 1546aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1547aa42240aSTomasz Figa * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1548aa42240aSTomasz Figa * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by 1549aa42240aSTomasz Figa * index. 1550aa42240aSTomasz Figa */ 1551aa42240aSTomasz Figa 1552aa42240aSTomasz Figa /** 1553aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1554aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1555aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1556aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1557aa42240aSTomasz Figa * into a PM domain. 1558aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1559aa42240aSTomasz Figa */ 1560aa42240aSTomasz Figa struct of_genpd_provider { 1561aa42240aSTomasz Figa struct list_head link; 1562aa42240aSTomasz Figa struct device_node *node; 1563aa42240aSTomasz Figa genpd_xlate_t xlate; 1564aa42240aSTomasz Figa void *data; 1565aa42240aSTomasz Figa }; 1566aa42240aSTomasz Figa 1567aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1568aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1569aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1570aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1571aa42240aSTomasz Figa 1572aa42240aSTomasz Figa /** 1573aa42240aSTomasz Figa * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping 1574aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1575aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1576aa42240aSTomasz Figa * 1577aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1578aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1579aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1580aa42240aSTomasz Figa */ 1581aa42240aSTomasz Figa struct generic_pm_domain *__of_genpd_xlate_simple( 1582aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1583aa42240aSTomasz Figa void *data) 1584aa42240aSTomasz Figa { 1585aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1586aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1587aa42240aSTomasz Figa return data; 1588aa42240aSTomasz Figa } 1589aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple); 1590aa42240aSTomasz Figa 1591aa42240aSTomasz Figa /** 1592aa42240aSTomasz Figa * __of_genpd_xlate_onecell() - Xlate function using a single index. 1593aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1594aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1595aa42240aSTomasz Figa * 1596aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1597aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1598aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1599aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1600aa42240aSTomasz Figa */ 1601aa42240aSTomasz Figa struct generic_pm_domain *__of_genpd_xlate_onecell( 1602aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1603aa42240aSTomasz Figa void *data) 1604aa42240aSTomasz Figa { 1605aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1606aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1607aa42240aSTomasz Figa 1608aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1609aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1610aa42240aSTomasz Figa 1611aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1612aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1613aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1614aa42240aSTomasz Figa } 1615aa42240aSTomasz Figa 1616aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1617aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1618aa42240aSTomasz Figa 1619aa42240aSTomasz Figa return genpd_data->domains[idx]; 1620aa42240aSTomasz Figa } 1621aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell); 1622aa42240aSTomasz Figa 1623aa42240aSTomasz Figa /** 1624aa42240aSTomasz Figa * __of_genpd_add_provider() - Register a PM domain provider for a node 1625aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1626aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1627aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1628aa42240aSTomasz Figa */ 1629aa42240aSTomasz Figa int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1630aa42240aSTomasz Figa void *data) 1631aa42240aSTomasz Figa { 1632aa42240aSTomasz Figa struct of_genpd_provider *cp; 1633aa42240aSTomasz Figa 1634aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1635aa42240aSTomasz Figa if (!cp) 1636aa42240aSTomasz Figa return -ENOMEM; 1637aa42240aSTomasz Figa 1638aa42240aSTomasz Figa cp->node = of_node_get(np); 1639aa42240aSTomasz Figa cp->data = data; 1640aa42240aSTomasz Figa cp->xlate = xlate; 1641aa42240aSTomasz Figa 1642aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1643aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1644aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1645aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1646aa42240aSTomasz Figa 1647aa42240aSTomasz Figa return 0; 1648aa42240aSTomasz Figa } 1649aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_add_provider); 1650aa42240aSTomasz Figa 1651aa42240aSTomasz Figa /** 1652aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1653aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1654aa42240aSTomasz Figa */ 1655aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1656aa42240aSTomasz Figa { 1657aa42240aSTomasz Figa struct of_genpd_provider *cp; 1658aa42240aSTomasz Figa 1659aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1660aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1661aa42240aSTomasz Figa if (cp->node == np) { 1662aa42240aSTomasz Figa list_del(&cp->link); 1663aa42240aSTomasz Figa of_node_put(cp->node); 1664aa42240aSTomasz Figa kfree(cp); 1665aa42240aSTomasz Figa break; 1666aa42240aSTomasz Figa } 1667aa42240aSTomasz Figa } 1668aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1669aa42240aSTomasz Figa } 1670aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1671aa42240aSTomasz Figa 1672aa42240aSTomasz Figa /** 1673aa42240aSTomasz Figa * of_genpd_get_from_provider() - Look-up PM domain 1674aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1675aa42240aSTomasz Figa * 1676aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1677aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1678aa42240aSTomasz Figa * domain. 1679aa42240aSTomasz Figa * 1680aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1681aa42240aSTomasz Figa * on failure. 1682aa42240aSTomasz Figa */ 16837496fcbeSAmit Daniel Kachhap struct generic_pm_domain *of_genpd_get_from_provider( 1684aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1685aa42240aSTomasz Figa { 1686aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1687aa42240aSTomasz Figa struct of_genpd_provider *provider; 1688aa42240aSTomasz Figa 1689aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1690aa42240aSTomasz Figa 1691aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1692aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1693aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1694aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1695aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1696aa42240aSTomasz Figa break; 1697aa42240aSTomasz Figa } 1698aa42240aSTomasz Figa 1699aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1700aa42240aSTomasz Figa 1701aa42240aSTomasz Figa return genpd; 1702aa42240aSTomasz Figa } 17037496fcbeSAmit Daniel Kachhap EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); 1704aa42240aSTomasz Figa 1705aa42240aSTomasz Figa /** 1706aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 17078bb6944eSJon Hunter * @dev: Device to detach. 1708aa42240aSTomasz Figa * @power_off: Currently not used 1709aa42240aSTomasz Figa * 1710aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1711aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1712aa42240aSTomasz Figa */ 1713aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1714aa42240aSTomasz Figa { 1715446d999cSRussell King struct generic_pm_domain *pd; 171693af5e93SGeert Uytterhoeven unsigned int i; 1717aa42240aSTomasz Figa int ret = 0; 1718aa42240aSTomasz Figa 1719446d999cSRussell King pd = pm_genpd_lookup_dev(dev); 1720aa42240aSTomasz Figa if (!pd) 1721aa42240aSTomasz Figa return; 1722aa42240aSTomasz Figa 1723aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1724aa42240aSTomasz Figa 172593af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1726aa42240aSTomasz Figa ret = pm_genpd_remove_device(pd, dev); 1727aa42240aSTomasz Figa if (ret != -EAGAIN) 1728aa42240aSTomasz Figa break; 172993af5e93SGeert Uytterhoeven 173093af5e93SGeert Uytterhoeven mdelay(i); 1731aa42240aSTomasz Figa cond_resched(); 1732aa42240aSTomasz Figa } 1733aa42240aSTomasz Figa 1734aa42240aSTomasz Figa if (ret < 0) { 1735aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1736aa42240aSTomasz Figa pd->name, ret); 1737aa42240aSTomasz Figa return; 1738aa42240aSTomasz Figa } 1739aa42240aSTomasz Figa 1740aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1741aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1742aa42240aSTomasz Figa } 1743aa42240aSTomasz Figa 1744632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1745632f7ce3SRussell King { 1746632f7ce3SRussell King struct generic_pm_domain *pd; 1747632f7ce3SRussell King 1748632f7ce3SRussell King pd = dev_to_genpd(dev); 1749632f7ce3SRussell King if (IS_ERR(pd)) 1750632f7ce3SRussell King return; 1751632f7ce3SRussell King 1752632f7ce3SRussell King genpd_queue_power_off_work(pd); 1753632f7ce3SRussell King } 1754632f7ce3SRussell King 1755aa42240aSTomasz Figa /** 1756aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1757aa42240aSTomasz Figa * @dev: Device to attach. 1758aa42240aSTomasz Figa * 1759aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1760aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1761aa42240aSTomasz Figa * 1762aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1763aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1764aa42240aSTomasz Figa * 1765311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1766311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1767311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1768311fa6adSJon Hunter * probed and to re-try again later. 1769aa42240aSTomasz Figa */ 1770aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 1771aa42240aSTomasz Figa { 1772aa42240aSTomasz Figa struct of_phandle_args pd_args; 1773aa42240aSTomasz Figa struct generic_pm_domain *pd; 177493af5e93SGeert Uytterhoeven unsigned int i; 1775aa42240aSTomasz Figa int ret; 1776aa42240aSTomasz Figa 1777aa42240aSTomasz Figa if (!dev->of_node) 1778aa42240aSTomasz Figa return -ENODEV; 1779aa42240aSTomasz Figa 1780aa42240aSTomasz Figa if (dev->pm_domain) 1781aa42240aSTomasz Figa return -EEXIST; 1782aa42240aSTomasz Figa 1783aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 1784aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 1785aa42240aSTomasz Figa if (ret < 0) { 1786aa42240aSTomasz Figa if (ret != -ENOENT) 1787aa42240aSTomasz Figa return ret; 1788aa42240aSTomasz Figa 1789aa42240aSTomasz Figa /* 1790aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 1791aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 1792aa42240aSTomasz Figa */ 1793aa42240aSTomasz Figa pd_args.args_count = 0; 1794aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 1795aa42240aSTomasz Figa "samsung,power-domain", 0); 1796aa42240aSTomasz Figa if (!pd_args.np) 1797aa42240aSTomasz Figa return -ENOENT; 1798aa42240aSTomasz Figa } 1799aa42240aSTomasz Figa 1800aa42240aSTomasz Figa pd = of_genpd_get_from_provider(&pd_args); 1801265e2cf6SEric Anholt of_node_put(pd_args.np); 1802aa42240aSTomasz Figa if (IS_ERR(pd)) { 1803aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 1804aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 1805311fa6adSJon Hunter return -EPROBE_DEFER; 1806aa42240aSTomasz Figa } 1807aa42240aSTomasz Figa 1808aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 1809aa42240aSTomasz Figa 181093af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1811aa42240aSTomasz Figa ret = pm_genpd_add_device(pd, dev); 1812aa42240aSTomasz Figa if (ret != -EAGAIN) 1813aa42240aSTomasz Figa break; 181493af5e93SGeert Uytterhoeven 181593af5e93SGeert Uytterhoeven mdelay(i); 1816aa42240aSTomasz Figa cond_resched(); 1817aa42240aSTomasz Figa } 1818aa42240aSTomasz Figa 1819aa42240aSTomasz Figa if (ret < 0) { 1820aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 1821aa42240aSTomasz Figa pd->name, ret); 1822311fa6adSJon Hunter goto out; 1823aa42240aSTomasz Figa } 1824aa42240aSTomasz Figa 1825aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 1826632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 1827aa42240aSTomasz Figa 182853af16f7SUlf Hansson mutex_lock(&pd->lock); 182953af16f7SUlf Hansson ret = genpd_poweron(pd, 0); 183053af16f7SUlf Hansson mutex_unlock(&pd->lock); 1831311fa6adSJon Hunter out: 1832311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 1833aa42240aSTomasz Figa } 1834aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 1835d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 18362bd5306aSMaciej Matraszek 18372bd5306aSMaciej Matraszek 18382bd5306aSMaciej Matraszek /*** debugfs support ***/ 18392bd5306aSMaciej Matraszek 18402bd5306aSMaciej Matraszek #ifdef CONFIG_PM_ADVANCED_DEBUG 18412bd5306aSMaciej Matraszek #include <linux/pm.h> 18422bd5306aSMaciej Matraszek #include <linux/device.h> 18432bd5306aSMaciej Matraszek #include <linux/debugfs.h> 18442bd5306aSMaciej Matraszek #include <linux/seq_file.h> 18452bd5306aSMaciej Matraszek #include <linux/init.h> 18462bd5306aSMaciej Matraszek #include <linux/kobject.h> 18472bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 18482bd5306aSMaciej Matraszek 18492bd5306aSMaciej Matraszek /* 18502bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 1851d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 18522bd5306aSMaciej Matraszek */ 18532bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 18542bd5306aSMaciej Matraszek { 18552bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 18562bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 18572bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 18582bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 18592bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 18602bd5306aSMaciej Matraszek }; 18612bd5306aSMaciej Matraszek const char *p = ""; 18622bd5306aSMaciej Matraszek 18632bd5306aSMaciej Matraszek if (dev->power.runtime_error) 18642bd5306aSMaciej Matraszek p = "error"; 18652bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 18662bd5306aSMaciej Matraszek p = "unsupported"; 18672bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 18682bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 18692bd5306aSMaciej Matraszek else 18702bd5306aSMaciej Matraszek WARN_ON(1); 18712bd5306aSMaciej Matraszek 18722bd5306aSMaciej Matraszek seq_puts(s, p); 18732bd5306aSMaciej Matraszek } 18742bd5306aSMaciej Matraszek 18752bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 187666a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 18772bd5306aSMaciej Matraszek { 18782bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 18792bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 18802bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 18812bd5306aSMaciej Matraszek }; 18822bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 18832bd5306aSMaciej Matraszek const char *kobj_path; 18842bd5306aSMaciej Matraszek struct gpd_link *link; 18856954d432SGeert Uytterhoeven char state[16]; 18862bd5306aSMaciej Matraszek int ret; 18872bd5306aSMaciej Matraszek 188866a5ca4bSKevin Hilman ret = mutex_lock_interruptible(&genpd->lock); 18892bd5306aSMaciej Matraszek if (ret) 18902bd5306aSMaciej Matraszek return -ERESTARTSYS; 18912bd5306aSMaciej Matraszek 189266a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 18932bd5306aSMaciej Matraszek goto exit; 1894fc5cbf0cSAxel Haslam if (genpd->status == GPD_STATE_POWER_OFF) 18956954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s %u", 18966954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 1897fc5cbf0cSAxel Haslam else 18986954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 18996954d432SGeert Uytterhoeven status_lookup[genpd->status]); 19006954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 19012bd5306aSMaciej Matraszek 19022bd5306aSMaciej Matraszek /* 19032bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 19042bd5306aSMaciej Matraszek * master and slave, so we are safe. 190566a5ca4bSKevin Hilman * Also genpd->name is immutable. 19062bd5306aSMaciej Matraszek */ 190766a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 19082bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 190966a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 19102bd5306aSMaciej Matraszek seq_puts(s, ", "); 19112bd5306aSMaciej Matraszek } 19122bd5306aSMaciej Matraszek 191366a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 19142bd5306aSMaciej Matraszek kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL); 19152bd5306aSMaciej Matraszek if (kobj_path == NULL) 19162bd5306aSMaciej Matraszek continue; 19172bd5306aSMaciej Matraszek 19182bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 19192bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 19202bd5306aSMaciej Matraszek kfree(kobj_path); 19212bd5306aSMaciej Matraszek } 19222bd5306aSMaciej Matraszek 19232bd5306aSMaciej Matraszek seq_puts(s, "\n"); 19242bd5306aSMaciej Matraszek exit: 192566a5ca4bSKevin Hilman mutex_unlock(&genpd->lock); 19262bd5306aSMaciej Matraszek 19272bd5306aSMaciej Matraszek return 0; 19282bd5306aSMaciej Matraszek } 19292bd5306aSMaciej Matraszek 19302bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 19312bd5306aSMaciej Matraszek { 193266a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 19332bd5306aSMaciej Matraszek int ret = 0; 19342bd5306aSMaciej Matraszek 19352bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 19362bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 19372bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 19382bd5306aSMaciej Matraszek 19392bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 19402bd5306aSMaciej Matraszek if (ret) 19412bd5306aSMaciej Matraszek return -ERESTARTSYS; 19422bd5306aSMaciej Matraszek 194366a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 194466a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 19452bd5306aSMaciej Matraszek if (ret) 19462bd5306aSMaciej Matraszek break; 19472bd5306aSMaciej Matraszek } 19482bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 19492bd5306aSMaciej Matraszek 19502bd5306aSMaciej Matraszek return ret; 19512bd5306aSMaciej Matraszek } 19522bd5306aSMaciej Matraszek 19532bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 19542bd5306aSMaciej Matraszek { 19552bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 19562bd5306aSMaciej Matraszek } 19572bd5306aSMaciej Matraszek 19582bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 19592bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 19602bd5306aSMaciej Matraszek .read = seq_read, 19612bd5306aSMaciej Matraszek .llseek = seq_lseek, 19622bd5306aSMaciej Matraszek .release = single_release, 19632bd5306aSMaciej Matraszek }; 19642bd5306aSMaciej Matraszek 19652bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 19662bd5306aSMaciej Matraszek { 19672bd5306aSMaciej Matraszek struct dentry *d; 19682bd5306aSMaciej Matraszek 19692bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 19702bd5306aSMaciej Matraszek 19712bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 19722bd5306aSMaciej Matraszek return -ENOMEM; 19732bd5306aSMaciej Matraszek 19742bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 19752bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 19762bd5306aSMaciej Matraszek if (!d) 19772bd5306aSMaciej Matraszek return -ENOMEM; 19782bd5306aSMaciej Matraszek 19792bd5306aSMaciej Matraszek return 0; 19802bd5306aSMaciej Matraszek } 19812bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 19822bd5306aSMaciej Matraszek 19832bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 19842bd5306aSMaciej Matraszek { 19852bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 19862bd5306aSMaciej Matraszek } 19872bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 19882bd5306aSMaciej Matraszek #endif /* CONFIG_PM_ADVANCED_DEBUG */ 1989