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 */ 48f58d4e5aSJon Hunter static struct generic_pm_domain *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 19039dd0f23SUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 1913f241775SRafael J. Wysocki return 0; 1925248051bSRafael J. Wysocki 1935063ce15SRafael J. Wysocki /* 1945063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 1955063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 1965063ce15SRafael J. Wysocki * with it. 1975063ce15SRafael J. Wysocki */ 1985063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1990106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 2005248051bSRafael J. Wysocki 2010106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 2020106ef51SMarek Szyprowski 2030106ef51SMarek Szyprowski mutex_lock_nested(&master->lock, depth + 1); 20453af16f7SUlf Hansson ret = genpd_poweron(master, depth + 1); 2050106ef51SMarek Szyprowski mutex_unlock(&master->lock); 2060106ef51SMarek Szyprowski 2075063ce15SRafael J. Wysocki if (ret) { 2080106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 2099e08cf42SRafael J. Wysocki goto err; 2105248051bSRafael J. Wysocki } 2115063ce15SRafael J. Wysocki } 2125248051bSRafael J. Wysocki 213a4630c61SGeert Uytterhoeven ret = genpd_power_on(genpd, true); 2149e08cf42SRafael J. Wysocki if (ret) 2159e08cf42SRafael J. Wysocki goto err; 2160140d8bdSRafael J. Wysocki 217ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 2183f241775SRafael J. Wysocki return 0; 2199e08cf42SRafael J. Wysocki 2209e08cf42SRafael J. Wysocki err: 22129e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 22229e47e21SUlf Hansson &genpd->slave_links, 22329e47e21SUlf Hansson slave_node) { 2245063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 22529e47e21SUlf Hansson genpd_queue_power_off_work(link->master); 22629e47e21SUlf Hansson } 2279e08cf42SRafael J. Wysocki 2283f241775SRafael J. Wysocki return ret; 2293f241775SRafael J. Wysocki } 2303f241775SRafael J. Wysocki 2316ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 2326ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 2336ff7bb0dSRafael J. Wysocki { 2346ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 2356ff7bb0dSRafael J. Wysocki struct device *dev; 2366ff7bb0dSRafael J. Wysocki 2376ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 2386ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 2396ff7bb0dSRafael J. Wysocki 2406ff7bb0dSRafael J. Wysocki for (;;) { 2416ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 2426ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 2436ff7bb0dSRafael J. Wysocki 2446ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 2456ff7bb0dSRafael J. Wysocki 2466ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 2476ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 2481d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 2496ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 2506ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 2516ff7bb0dSRafael J. Wysocki } else { 2526ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 2536ff7bb0dSRafael J. Wysocki } 2546ff7bb0dSRafael J. Wysocki 2556ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 2566ff7bb0dSRafael J. Wysocki 2576ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 2586ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 2596ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2606ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 2616ff7bb0dSRafael J. Wysocki } 2626ff7bb0dSRafael J. Wysocki 2636ff7bb0dSRafael J. Wysocki dev = dev->parent; 2646ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 2656ff7bb0dSRafael J. Wysocki break; 2666ff7bb0dSRafael J. Wysocki } 2676ff7bb0dSRafael J. Wysocki 2686ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2696ff7bb0dSRafael J. Wysocki } 2706ff7bb0dSRafael J. Wysocki 2715248051bSRafael J. Wysocki /** 2727420aa4fSUlf Hansson * genpd_poweroff - Remove power from a given PM domain. 273f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 274f96b3c4fSUlf Hansson * @is_async: PM domain is powered down from a scheduled work 275f721889fSRafael J. Wysocki * 276f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 277ba2bbfbfSUlf Hansson * have been powered down, remove power from @genpd. 278f721889fSRafael J. Wysocki */ 2797420aa4fSUlf Hansson static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) 280f721889fSRafael J. Wysocki { 2814605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 2825063ce15SRafael J. Wysocki struct gpd_link *link; 283ba2bbfbfSUlf Hansson unsigned int not_suspended = 0; 284f721889fSRafael J. Wysocki 285c6d22b37SRafael J. Wysocki /* 286c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 287c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 288ba2bbfbfSUlf Hansson * (2) System suspend is in progress. 289c6d22b37SRafael J. Wysocki */ 2903f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 291ba2bbfbfSUlf Hansson || genpd->prepared_count > 0) 292f721889fSRafael J. Wysocki return 0; 293f721889fSRafael J. Wysocki 294c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 295f721889fSRafael J. Wysocki return -EBUSY; 296f721889fSRafael J. Wysocki 29734b1f762SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) { 29834b1f762SRafael J. Wysocki enum pm_qos_flags_status stat; 29934b1f762SRafael J. Wysocki 30034b1f762SRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, 30134b1f762SRafael J. Wysocki PM_QOS_FLAG_NO_POWER_OFF 30234b1f762SRafael J. Wysocki | PM_QOS_FLAG_REMOTE_WAKEUP); 30334b1f762SRafael J. Wysocki if (stat > PM_QOS_FLAGS_NONE) 30434b1f762SRafael J. Wysocki return -EBUSY; 30534b1f762SRafael J. Wysocki 306298cd0f0SLina Iyer if (!pm_runtime_suspended(pdd->dev) || pdd->dev->power.irq_safe) 307f721889fSRafael J. Wysocki not_suspended++; 30834b1f762SRafael J. Wysocki } 309f721889fSRafael J. Wysocki 310f96b3c4fSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && is_async)) 311f721889fSRafael J. Wysocki return -EBUSY; 312f721889fSRafael J. Wysocki 313f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 314f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 315f721889fSRafael J. Wysocki return -EAGAIN; 316f721889fSRafael J. Wysocki } 317f721889fSRafael J. Wysocki 3183c07cbc4SRafael J. Wysocki if (genpd->power_off) { 319ba2bbfbfSUlf Hansson int ret; 320ba2bbfbfSUlf Hansson 321ba2bbfbfSUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 322ba2bbfbfSUlf Hansson return -EBUSY; 32317b75ecaSRafael J. Wysocki 3243c07cbc4SRafael J. Wysocki /* 3255063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 3267420aa4fSUlf Hansson * managed to call genpd_poweron() for the master yet after 3277420aa4fSUlf Hansson * incrementing it. In that case genpd_poweron() will wait 3283c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 3297420aa4fSUlf Hansson * the genpd_poweron() restore power for us (this shouldn't 3303c07cbc4SRafael J. Wysocki * happen very often). 3313c07cbc4SRafael J. Wysocki */ 332a4630c61SGeert Uytterhoeven ret = genpd_power_off(genpd, true); 333ba2bbfbfSUlf Hansson if (ret) 334ba2bbfbfSUlf Hansson return ret; 335d2805402SRafael J. Wysocki } 336f721889fSRafael J. Wysocki 33717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 338221e9b58SRafael J. Wysocki 3395063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 3405063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 3415063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 3425063ce15SRafael J. Wysocki } 34317b75ecaSRafael J. Wysocki 344ba2bbfbfSUlf Hansson return 0; 345f721889fSRafael J. Wysocki } 346f721889fSRafael J. Wysocki 347f721889fSRafael J. Wysocki /** 348f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 349f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 350f721889fSRafael J. Wysocki */ 351f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 352f721889fSRafael J. Wysocki { 353f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 354f721889fSRafael J. Wysocki 355f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 356f721889fSRafael J. Wysocki 357ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 3587420aa4fSUlf Hansson genpd_poweroff(genpd, true); 359ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 360f721889fSRafael J. Wysocki } 361f721889fSRafael J. Wysocki 362f721889fSRafael J. Wysocki /** 36354eeddbfSUlf Hansson * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 36454eeddbfSUlf Hansson * @dev: Device to handle. 36554eeddbfSUlf Hansson */ 36654eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev) 36754eeddbfSUlf Hansson { 36854eeddbfSUlf Hansson int (*cb)(struct device *__dev); 36954eeddbfSUlf Hansson 37054eeddbfSUlf Hansson if (dev->type && dev->type->pm) 37154eeddbfSUlf Hansson cb = dev->type->pm->runtime_suspend; 37254eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 37354eeddbfSUlf Hansson cb = dev->class->pm->runtime_suspend; 37454eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 37554eeddbfSUlf Hansson cb = dev->bus->pm->runtime_suspend; 37654eeddbfSUlf Hansson else 37754eeddbfSUlf Hansson cb = NULL; 37854eeddbfSUlf Hansson 37954eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 38054eeddbfSUlf Hansson cb = dev->driver->pm->runtime_suspend; 38154eeddbfSUlf Hansson 38254eeddbfSUlf Hansson return cb ? cb(dev) : 0; 38354eeddbfSUlf Hansson } 38454eeddbfSUlf Hansson 38554eeddbfSUlf Hansson /** 38654eeddbfSUlf Hansson * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 38754eeddbfSUlf Hansson * @dev: Device to handle. 38854eeddbfSUlf Hansson */ 38954eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev) 39054eeddbfSUlf Hansson { 39154eeddbfSUlf Hansson int (*cb)(struct device *__dev); 39254eeddbfSUlf Hansson 39354eeddbfSUlf Hansson if (dev->type && dev->type->pm) 39454eeddbfSUlf Hansson cb = dev->type->pm->runtime_resume; 39554eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 39654eeddbfSUlf Hansson cb = dev->class->pm->runtime_resume; 39754eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 39854eeddbfSUlf Hansson cb = dev->bus->pm->runtime_resume; 39954eeddbfSUlf Hansson else 40054eeddbfSUlf Hansson cb = NULL; 40154eeddbfSUlf Hansson 40254eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 40354eeddbfSUlf Hansson cb = dev->driver->pm->runtime_resume; 40454eeddbfSUlf Hansson 40554eeddbfSUlf Hansson return cb ? cb(dev) : 0; 40654eeddbfSUlf Hansson } 40754eeddbfSUlf Hansson 40854eeddbfSUlf Hansson /** 409795bd2e7SUlf Hansson * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 410f721889fSRafael J. Wysocki * @dev: Device to suspend. 411f721889fSRafael J. Wysocki * 412f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 413f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 414f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 415f721889fSRafael J. Wysocki */ 416795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev) 417f721889fSRafael J. Wysocki { 418f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 4199df3921eSUlf Hansson bool (*suspend_ok)(struct device *__dev); 4202b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 421ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 4222b1d88cdSUlf Hansson ktime_t time_start; 4232b1d88cdSUlf Hansson s64 elapsed_ns; 424d5e4cbfeSRafael J. Wysocki int ret; 425f721889fSRafael J. Wysocki 426f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 427f721889fSRafael J. Wysocki 4285248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4295248051bSRafael J. Wysocki if (IS_ERR(genpd)) 430f721889fSRafael J. Wysocki return -EINVAL; 431f721889fSRafael J. Wysocki 432ffe12855SUlf Hansson /* 433ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 434ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 435ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 436ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 437ffe12855SUlf Hansson */ 4389df3921eSUlf Hansson suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 4399df3921eSUlf Hansson if (runtime_pm && suspend_ok && !suspend_ok(dev)) 440b02c999aSRafael J. Wysocki return -EBUSY; 441b02c999aSRafael J. Wysocki 4422b1d88cdSUlf Hansson /* Measure suspend latency. */ 443ffe12855SUlf Hansson if (runtime_pm) 4442b1d88cdSUlf Hansson time_start = ktime_get(); 4452b1d88cdSUlf Hansson 44654eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 447f721889fSRafael J. Wysocki if (ret) 44817b75ecaSRafael J. Wysocki return ret; 44917b75ecaSRafael J. Wysocki 4502b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 451ba2bbfbfSUlf Hansson if (ret) { 45254eeddbfSUlf Hansson __genpd_runtime_resume(dev); 453ba2bbfbfSUlf Hansson return ret; 454ba2bbfbfSUlf Hansson } 455ba2bbfbfSUlf Hansson 4562b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 457ffe12855SUlf Hansson if (runtime_pm) { 4582b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 4592b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 4602b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 4612b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 4622b1d88cdSUlf Hansson elapsed_ns); 4632b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 4642b1d88cdSUlf Hansson td->constraint_changed = true; 4652b1d88cdSUlf Hansson } 466ffe12855SUlf Hansson } 4672b1d88cdSUlf Hansson 4680aa2a221SRafael J. Wysocki /* 4690aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 4700aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 4710aa2a221SRafael J. Wysocki */ 4720aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 4730aa2a221SRafael J. Wysocki return 0; 4740aa2a221SRafael J. Wysocki 475c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 4767420aa4fSUlf Hansson genpd_poweroff(genpd, false); 477c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 478f721889fSRafael J. Wysocki 479f721889fSRafael J. Wysocki return 0; 480f721889fSRafael J. Wysocki } 481f721889fSRafael J. Wysocki 482f721889fSRafael J. Wysocki /** 483795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 484f721889fSRafael J. Wysocki * @dev: Device to resume. 485f721889fSRafael J. Wysocki * 486f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 487f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 488f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 489f721889fSRafael J. Wysocki */ 490795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 491f721889fSRafael J. Wysocki { 492f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 4932b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 494ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 4952b1d88cdSUlf Hansson ktime_t time_start; 4962b1d88cdSUlf Hansson s64 elapsed_ns; 497f721889fSRafael J. Wysocki int ret; 498ba2bbfbfSUlf Hansson bool timed = true; 499f721889fSRafael J. Wysocki 500f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 501f721889fSRafael J. Wysocki 5025248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5035248051bSRafael J. Wysocki if (IS_ERR(genpd)) 504f721889fSRafael J. Wysocki return -EINVAL; 505f721889fSRafael J. Wysocki 5060aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 507ba2bbfbfSUlf Hansson if (dev->power.irq_safe) { 508ba2bbfbfSUlf Hansson timed = false; 509ba2bbfbfSUlf Hansson goto out; 510ba2bbfbfSUlf Hansson } 5110aa2a221SRafael J. Wysocki 512c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 51353af16f7SUlf Hansson ret = genpd_poweron(genpd, 0); 5143f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 515ba2bbfbfSUlf Hansson 516ba2bbfbfSUlf Hansson if (ret) 5173f241775SRafael J. Wysocki return ret; 518c6d22b37SRafael J. Wysocki 519ba2bbfbfSUlf Hansson out: 5202b1d88cdSUlf Hansson /* Measure resume latency. */ 521ffe12855SUlf Hansson if (timed && runtime_pm) 5222b1d88cdSUlf Hansson time_start = ktime_get(); 5232b1d88cdSUlf Hansson 524076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 525076395caSLaurent Pinchart if (ret) 526076395caSLaurent Pinchart goto err_poweroff; 527076395caSLaurent Pinchart 52854eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 529076395caSLaurent Pinchart if (ret) 530076395caSLaurent Pinchart goto err_stop; 5312b1d88cdSUlf Hansson 5322b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 533ffe12855SUlf Hansson if (timed && runtime_pm) { 5342b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5352b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 5362b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 5372b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 5382b1d88cdSUlf Hansson elapsed_ns); 5392b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5402b1d88cdSUlf Hansson td->constraint_changed = true; 5412b1d88cdSUlf Hansson } 5422b1d88cdSUlf Hansson } 54317b75ecaSRafael J. Wysocki 544f721889fSRafael J. Wysocki return 0; 545076395caSLaurent Pinchart 546076395caSLaurent Pinchart err_stop: 547076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 548076395caSLaurent Pinchart err_poweroff: 549076395caSLaurent Pinchart if (!dev->power.irq_safe) { 550076395caSLaurent Pinchart mutex_lock(&genpd->lock); 551076395caSLaurent Pinchart genpd_poweroff(genpd, 0); 552076395caSLaurent Pinchart mutex_unlock(&genpd->lock); 553076395caSLaurent Pinchart } 554076395caSLaurent Pinchart 555076395caSLaurent Pinchart return ret; 556f721889fSRafael J. Wysocki } 557f721889fSRafael J. Wysocki 55839ac5ba5STushar Behera static bool pd_ignore_unused; 55939ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 56039ac5ba5STushar Behera { 56139ac5ba5STushar Behera pd_ignore_unused = true; 56239ac5ba5STushar Behera return 1; 56339ac5ba5STushar Behera } 56439ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 56539ac5ba5STushar Behera 56617f2ae7fSRafael J. Wysocki /** 567bb4b72fcSUlf Hansson * genpd_poweroff_unused - Power off all PM domains with no devices in use. 56817f2ae7fSRafael J. Wysocki */ 569bb4b72fcSUlf Hansson static int __init genpd_poweroff_unused(void) 57017f2ae7fSRafael J. Wysocki { 57117f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 57217f2ae7fSRafael J. Wysocki 57339ac5ba5STushar Behera if (pd_ignore_unused) { 57439ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 575bb4b72fcSUlf Hansson return 0; 57639ac5ba5STushar Behera } 57739ac5ba5STushar Behera 57817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 57917f2ae7fSRafael J. Wysocki 58017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 58117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 58217f2ae7fSRafael J. Wysocki 58317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 58417f2ae7fSRafael J. Wysocki 5852fe71dcdSUlf Hansson return 0; 5862fe71dcdSUlf Hansson } 5872fe71dcdSUlf Hansson late_initcall(genpd_poweroff_unused); 5882fe71dcdSUlf Hansson 5890159ec67SJon Hunter #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) 590596ba34bSRafael J. Wysocki 59177f827deSRafael J. Wysocki /** 59277f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 59377f827deSRafael J. Wysocki * @genpd: PM domain to check. 59477f827deSRafael J. Wysocki */ 595895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 59677f827deSRafael J. Wysocki { 597895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 59877f827deSRafael J. Wysocki 59977f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 60077f827deSRafael J. Wysocki return false; 60177f827deSRafael J. Wysocki 60277f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 60377f827deSRafael J. Wysocki if (gpd == genpd) 60477f827deSRafael J. Wysocki return true; 60577f827deSRafael J. Wysocki 60677f827deSRafael J. Wysocki return false; 60777f827deSRafael J. Wysocki } 60877f827deSRafael J. Wysocki 6090159ec67SJon Hunter #endif 6100159ec67SJon Hunter 6110159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 6120159ec67SJon Hunter 613d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 614d5e4cbfeSRafael J. Wysocki struct device *dev) 615d5e4cbfeSRafael J. Wysocki { 616d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 617d5e4cbfeSRafael J. Wysocki } 618d5e4cbfeSRafael J. Wysocki 619596ba34bSRafael J. Wysocki /** 6205063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 621596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 622596ba34bSRafael J. Wysocki * 623596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 6245063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 625596ba34bSRafael J. Wysocki * 62677f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 62777f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 62877f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 62977f827deSRafael J. Wysocki * parallel). 630596ba34bSRafael J. Wysocki */ 631adb560b3SUlf Hansson static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 632596ba34bSRafael J. Wysocki { 6335063ce15SRafael J. Wysocki struct gpd_link *link; 634596ba34bSRafael J. Wysocki 63517b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 636596ba34bSRafael J. Wysocki return; 637596ba34bSRafael J. Wysocki 638c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 639c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 640596ba34bSRafael J. Wysocki return; 641596ba34bSRafael J. Wysocki 642fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 643fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 644adb560b3SUlf Hansson genpd_power_off(genpd, false); 645596ba34bSRafael J. Wysocki 64617b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 6475063ce15SRafael J. Wysocki 6485063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 6495063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 650adb560b3SUlf Hansson pm_genpd_sync_poweroff(link->master); 651596ba34bSRafael J. Wysocki } 652596ba34bSRafael J. Wysocki } 653596ba34bSRafael J. Wysocki 654596ba34bSRafael J. Wysocki /** 655802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 656802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 657802d8b49SRafael J. Wysocki * 65877f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 65977f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 66077f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 66177f827deSRafael J. Wysocki * parallel). 662802d8b49SRafael J. Wysocki */ 663adb560b3SUlf Hansson static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) 664802d8b49SRafael J. Wysocki { 665802d8b49SRafael J. Wysocki struct gpd_link *link; 666802d8b49SRafael J. Wysocki 667ba2bbfbfSUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 668802d8b49SRafael J. Wysocki return; 669802d8b49SRafael J. Wysocki 670802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 671adb560b3SUlf Hansson pm_genpd_sync_poweron(link->master); 672802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 673802d8b49SRafael J. Wysocki } 674802d8b49SRafael J. Wysocki 675adb560b3SUlf Hansson genpd_power_on(genpd, false); 676802d8b49SRafael J. Wysocki 677802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 678802d8b49SRafael J. Wysocki } 679802d8b49SRafael J. Wysocki 680802d8b49SRafael J. Wysocki /** 6814ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 6824ecd6e65SRafael J. Wysocki * @dev: Device to check. 6834ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 6844ecd6e65SRafael J. Wysocki * 6854ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 6864ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 6874ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 6884ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 6894ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 6904ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 6914ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 6924ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 6934ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 6944ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 6954ecd6e65SRafael J. Wysocki */ 6964ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 6974ecd6e65SRafael J. Wysocki { 6984ecd6e65SRafael J. Wysocki bool active_wakeup; 6994ecd6e65SRafael J. Wysocki 7004ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 7014ecd6e65SRafael J. Wysocki return false; 7024ecd6e65SRafael J. Wysocki 703d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 7044ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 7054ecd6e65SRafael J. Wysocki } 7064ecd6e65SRafael J. Wysocki 7074ecd6e65SRafael J. Wysocki /** 708596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 709596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 710596ba34bSRafael J. Wysocki * 711596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 712596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 713596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 714596ba34bSRafael J. Wysocki * consisting of I/O devices. 715596ba34bSRafael J. Wysocki */ 716596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 717596ba34bSRafael J. Wysocki { 718596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 719b6c10c84SRafael J. Wysocki int ret; 720596ba34bSRafael J. Wysocki 721596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 722596ba34bSRafael J. Wysocki 723596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 724596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 725596ba34bSRafael J. Wysocki return -EINVAL; 726596ba34bSRafael J. Wysocki 72717b75ecaSRafael J. Wysocki /* 72817b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 72917b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 73017b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 73117b75ecaSRafael J. Wysocki */ 7324ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 7334ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 7344ecd6e65SRafael J. Wysocki 735ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 736596ba34bSRafael J. Wysocki 73739dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 73865533bbfSRafael J. Wysocki genpd->suspended_count = 0; 73917b75ecaSRafael J. Wysocki 740ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 741596ba34bSRafael J. Wysocki 742b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 743b6c10c84SRafael J. Wysocki if (ret) { 744b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 745b6c10c84SRafael J. Wysocki 74639dd0f23SUlf Hansson genpd->prepared_count--; 747b6c10c84SRafael J. Wysocki 748b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 749b6c10c84SRafael J. Wysocki } 75017b75ecaSRafael J. Wysocki 751b6c10c84SRafael J. Wysocki return ret; 752596ba34bSRafael J. Wysocki } 753596ba34bSRafael J. Wysocki 754596ba34bSRafael J. Wysocki /** 7550496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 7560496c8aeSRafael J. Wysocki * @dev: Device to suspend. 7570496c8aeSRafael J. Wysocki * 7580496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 7590496c8aeSRafael J. Wysocki * been stopped. 7600496c8aeSRafael J. Wysocki */ 7610496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 7620496c8aeSRafael J. Wysocki { 7630496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 764122a2237SUlf Hansson int ret; 765596ba34bSRafael J. Wysocki 7660496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 7670496c8aeSRafael J. Wysocki 7680496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 7690496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 7700496c8aeSRafael J. Wysocki return -EINVAL; 7710496c8aeSRafael J. Wysocki 77239dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 773d4f2d87aSRafael J. Wysocki return 0; 774d4f2d87aSRafael J. Wysocki 775122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) { 776122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 777122a2237SUlf Hansson if (ret) 778122a2237SUlf Hansson return ret; 779122a2237SUlf Hansson } 780122a2237SUlf Hansson 781596ba34bSRafael J. Wysocki /* 782596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 783596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 784596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 785596ba34bSRafael J. Wysocki */ 786596ba34bSRafael J. Wysocki genpd->suspended_count++; 787adb560b3SUlf Hansson pm_genpd_sync_poweroff(genpd); 788596ba34bSRafael J. Wysocki 789596ba34bSRafael J. Wysocki return 0; 790596ba34bSRafael J. Wysocki } 791596ba34bSRafael J. Wysocki 792596ba34bSRafael J. Wysocki /** 7930496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 794596ba34bSRafael J. Wysocki * @dev: Device to resume. 795596ba34bSRafael J. Wysocki * 7960496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 797596ba34bSRafael J. Wysocki */ 798596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 799596ba34bSRafael J. Wysocki { 800596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 801122a2237SUlf Hansson int ret = 0; 802596ba34bSRafael J. Wysocki 803596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 804596ba34bSRafael J. Wysocki 805596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 806596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 807596ba34bSRafael J. Wysocki return -EINVAL; 808596ba34bSRafael J. Wysocki 80939dd0f23SUlf Hansson if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) 810596ba34bSRafael J. Wysocki return 0; 811596ba34bSRafael J. Wysocki 812596ba34bSRafael J. Wysocki /* 813596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 814596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 815596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 816596ba34bSRafael J. Wysocki */ 817adb560b3SUlf Hansson pm_genpd_sync_poweron(genpd); 818596ba34bSRafael J. Wysocki genpd->suspended_count--; 819596ba34bSRafael J. Wysocki 820122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 821122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 822122a2237SUlf Hansson 823122a2237SUlf Hansson return ret; 824596ba34bSRafael J. Wysocki } 825596ba34bSRafael J. Wysocki 826596ba34bSRafael J. Wysocki /** 8270496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 828596ba34bSRafael J. Wysocki * @dev: Device to freeze. 829596ba34bSRafael J. Wysocki * 830596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 831596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 832596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 833596ba34bSRafael J. Wysocki * devices. 834596ba34bSRafael J. Wysocki */ 835596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 836596ba34bSRafael J. Wysocki { 837596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 838122a2237SUlf Hansson int ret = 0; 839596ba34bSRafael J. Wysocki 840596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 841596ba34bSRafael J. Wysocki 842596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 843596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 844596ba34bSRafael J. Wysocki return -EINVAL; 845596ba34bSRafael J. Wysocki 846122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 847122a2237SUlf Hansson ret = pm_runtime_force_suspend(dev); 848122a2237SUlf Hansson 849122a2237SUlf Hansson return ret; 850596ba34bSRafael J. Wysocki } 851596ba34bSRafael J. Wysocki 852596ba34bSRafael J. Wysocki /** 8530496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 854596ba34bSRafael J. Wysocki * @dev: Device to thaw. 855596ba34bSRafael J. Wysocki * 8560496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 8570496c8aeSRafael J. Wysocki * before the system transition. 858596ba34bSRafael J. Wysocki */ 859596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 860596ba34bSRafael J. Wysocki { 861596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 862122a2237SUlf Hansson int ret = 0; 863596ba34bSRafael J. Wysocki 864596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 865596ba34bSRafael J. Wysocki 866596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 867596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 868596ba34bSRafael J. Wysocki return -EINVAL; 869596ba34bSRafael J. Wysocki 870122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 871122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 872122a2237SUlf Hansson 873122a2237SUlf Hansson return ret; 8740496c8aeSRafael J. Wysocki } 875596ba34bSRafael J. Wysocki 8760496c8aeSRafael J. Wysocki /** 8770496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 878596ba34bSRafael J. Wysocki * @dev: Device to resume. 879596ba34bSRafael J. Wysocki * 8800496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 8810496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 882596ba34bSRafael J. Wysocki */ 883596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 884596ba34bSRafael J. Wysocki { 885596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 886122a2237SUlf Hansson int ret = 0; 887596ba34bSRafael J. Wysocki 888596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 889596ba34bSRafael J. Wysocki 890596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 891596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 892596ba34bSRafael J. Wysocki return -EINVAL; 893596ba34bSRafael J. Wysocki 894596ba34bSRafael J. Wysocki /* 895596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 896596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 897596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 89865533bbfSRafael J. Wysocki * 89965533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 90065533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 90165533bbfSRafael J. Wysocki */ 90239dd0f23SUlf Hansson if (genpd->suspended_count++ == 0) 90365533bbfSRafael J. Wysocki /* 90465533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 905802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 906802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 907596ba34bSRafael J. Wysocki */ 90817b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 90918dd2eceSRafael J. Wysocki 910adb560b3SUlf Hansson pm_genpd_sync_poweron(genpd); 911596ba34bSRafael J. Wysocki 912122a2237SUlf Hansson if (genpd->dev_ops.stop && genpd->dev_ops.start) 913122a2237SUlf Hansson ret = pm_runtime_force_resume(dev); 914122a2237SUlf Hansson 915122a2237SUlf Hansson return ret; 916596ba34bSRafael J. Wysocki } 917596ba34bSRafael J. Wysocki 918596ba34bSRafael J. Wysocki /** 919596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 920596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 921596ba34bSRafael J. Wysocki * 922596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 923596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 924596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 925596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 926596ba34bSRafael J. Wysocki */ 927596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 928596ba34bSRafael J. Wysocki { 929596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 930596ba34bSRafael J. Wysocki 931596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 932596ba34bSRafael J. Wysocki 933596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 934596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 935596ba34bSRafael J. Wysocki return; 936596ba34bSRafael J. Wysocki 9374d23a5e8SUlf Hansson pm_generic_complete(dev); 9384d23a5e8SUlf Hansson 939596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 940596ba34bSRafael J. Wysocki 94139dd0f23SUlf Hansson genpd->prepared_count--; 9424d23a5e8SUlf Hansson if (!genpd->prepared_count) 9434d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 944596ba34bSRafael J. Wysocki 945596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 946596ba34bSRafael J. Wysocki } 947596ba34bSRafael J. Wysocki 94877f827deSRafael J. Wysocki /** 949d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 95077f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 95177f827deSRafael J. Wysocki * 95277f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 95377f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 95477f827deSRafael J. Wysocki */ 955d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 95677f827deSRafael J. Wysocki { 95777f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 95877f827deSRafael J. Wysocki 95977f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 96077f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 96177f827deSRafael J. Wysocki return; 96277f827deSRafael J. Wysocki 96377f827deSRafael J. Wysocki if (suspend) { 96477f827deSRafael J. Wysocki genpd->suspended_count++; 965adb560b3SUlf Hansson pm_genpd_sync_poweroff(genpd); 96677f827deSRafael J. Wysocki } else { 967adb560b3SUlf Hansson pm_genpd_sync_poweron(genpd); 96877f827deSRafael J. Wysocki genpd->suspended_count--; 96977f827deSRafael J. Wysocki } 97077f827deSRafael J. Wysocki } 971d47e6464SUlf Hansson 972d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 973d47e6464SUlf Hansson { 974d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 975d47e6464SUlf Hansson } 976d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 977d47e6464SUlf Hansson 978d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 979d47e6464SUlf Hansson { 980d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 981d47e6464SUlf Hansson } 982d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 98377f827deSRafael J. Wysocki 984d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 985596ba34bSRafael J. Wysocki 986596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 987596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 988596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 989596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 990596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 991596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 992596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 993596ba34bSRafael J. Wysocki 994596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 995596ba34bSRafael J. Wysocki 996f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 997f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 998f104e1e5SUlf Hansson struct gpd_timing_data *td) 9991d5fcfecSRafael J. Wysocki { 10001d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 10013e235685SUlf Hansson int ret; 10023e235685SUlf Hansson 10033e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 10043e235685SUlf Hansson if (ret) 10053e235685SUlf Hansson return ERR_PTR(ret); 10061d5fcfecSRafael J. Wysocki 10071d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 10083e235685SUlf Hansson if (!gpd_data) { 10093e235685SUlf Hansson ret = -ENOMEM; 10103e235685SUlf Hansson goto err_put; 10113e235685SUlf Hansson } 10121d5fcfecSRafael J. Wysocki 1013f104e1e5SUlf Hansson if (td) 1014f104e1e5SUlf Hansson gpd_data->td = *td; 1015f104e1e5SUlf Hansson 1016f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1017f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1018f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1019f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1020f104e1e5SUlf Hansson 1021f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1022f104e1e5SUlf Hansson 1023f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1024f104e1e5SUlf Hansson ret = -EINVAL; 1025f104e1e5SUlf Hansson goto err_free; 1026f104e1e5SUlf Hansson } 1027f104e1e5SUlf Hansson 1028f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1029f104e1e5SUlf Hansson 1030f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1031f104e1e5SUlf Hansson 1032989561deSTomeu Vizoso dev_pm_domain_set(dev, &genpd->domain); 1033989561deSTomeu Vizoso 10341d5fcfecSRafael J. Wysocki return gpd_data; 10353e235685SUlf Hansson 1036f104e1e5SUlf Hansson err_free: 1037f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1038f104e1e5SUlf Hansson kfree(gpd_data); 10393e235685SUlf Hansson err_put: 10403e235685SUlf Hansson dev_pm_put_subsys_data(dev); 10413e235685SUlf Hansson return ERR_PTR(ret); 10421d5fcfecSRafael J. Wysocki } 10431d5fcfecSRafael J. Wysocki 104449d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 10451d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 10461d5fcfecSRafael J. Wysocki { 1047989561deSTomeu Vizoso dev_pm_domain_set(dev, NULL); 1048989561deSTomeu Vizoso 1049f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1050f104e1e5SUlf Hansson 1051f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1052f104e1e5SUlf Hansson 1053f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1054f104e1e5SUlf Hansson 10551d5fcfecSRafael J. Wysocki kfree(gpd_data); 10563e235685SUlf Hansson dev_pm_put_subsys_data(dev); 10571d5fcfecSRafael J. Wysocki } 10581d5fcfecSRafael J. Wysocki 105919efa5ffSJon Hunter static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1060b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1061f721889fSRafael J. Wysocki { 1062c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1063f721889fSRafael J. Wysocki int ret = 0; 1064f721889fSRafael J. Wysocki 1065f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1066f721889fSRafael J. Wysocki 1067f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1068f721889fSRafael J. Wysocki return -EINVAL; 1069f721889fSRafael J. Wysocki 1070f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 10713e235685SUlf Hansson if (IS_ERR(gpd_data)) 10723e235685SUlf Hansson return PTR_ERR(gpd_data); 10736ff7bb0dSRafael J. Wysocki 1074ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1075f721889fSRafael J. Wysocki 1076596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1077596ba34bSRafael J. Wysocki ret = -EAGAIN; 1078596ba34bSRafael J. Wysocki goto out; 1079596ba34bSRafael J. Wysocki } 1080596ba34bSRafael J. Wysocki 1081b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1082b472c2faSUlf Hansson if (ret) 1083b472c2faSUlf Hansson goto out; 1084d79b6fe1SGeert Uytterhoeven 108514b53064SUlf Hansson genpd->device_count++; 108614b53064SUlf Hansson genpd->max_off_time_changed = true; 108714b53064SUlf Hansson 10881d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 10896ff7bb0dSRafael J. Wysocki 1090f721889fSRafael J. Wysocki out: 1091ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1092f721889fSRafael J. Wysocki 1093c0356db7SUlf Hansson if (ret) 1094c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1095c0356db7SUlf Hansson else 1096c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 10971d5fcfecSRafael J. Wysocki 1098f721889fSRafael J. Wysocki return ret; 1099f721889fSRafael J. Wysocki } 110019efa5ffSJon Hunter 110119efa5ffSJon Hunter /** 110219efa5ffSJon Hunter * __pm_genpd_add_device - Add a device to an I/O PM domain. 110319efa5ffSJon Hunter * @genpd: PM domain to add the device to. 110419efa5ffSJon Hunter * @dev: Device to be added. 110519efa5ffSJon Hunter * @td: Set of PM QoS timing parameters to attach to the device. 110619efa5ffSJon Hunter */ 110719efa5ffSJon Hunter int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 110819efa5ffSJon Hunter struct gpd_timing_data *td) 110919efa5ffSJon Hunter { 111019efa5ffSJon Hunter int ret; 111119efa5ffSJon Hunter 111219efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 111319efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, td); 111419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 111519efa5ffSJon Hunter 111619efa5ffSJon Hunter return ret; 111719efa5ffSJon Hunter } 111824c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1119f721889fSRafael J. Wysocki 112085168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1121f721889fSRafael J. Wysocki struct device *dev) 1122f721889fSRafael J. Wysocki { 11236ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11244605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1125efa69025SRafael J. Wysocki int ret = 0; 1126f721889fSRafael J. Wysocki 1127f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1128f721889fSRafael J. Wysocki 1129c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1130c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1131c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1132c0356db7SUlf Hansson 1133ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1134f721889fSRafael J. Wysocki 1135596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1136596ba34bSRafael J. Wysocki ret = -EAGAIN; 1137596ba34bSRafael J. Wysocki goto out; 1138596ba34bSRafael J. Wysocki } 1139596ba34bSRafael J. Wysocki 11406ff7bb0dSRafael J. Wysocki genpd->device_count--; 11416ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 11426ff7bb0dSRafael J. Wysocki 1143d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1144c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1145d79b6fe1SGeert Uytterhoeven 1146efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 11476ff7bb0dSRafael J. Wysocki 1148ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 11496ff7bb0dSRafael J. Wysocki 115049d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 11511d5fcfecSRafael J. Wysocki 11526ff7bb0dSRafael J. Wysocki return 0; 1153f721889fSRafael J. Wysocki 1154596ba34bSRafael J. Wysocki out: 1155ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1156c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1157f721889fSRafael J. Wysocki 1158f721889fSRafael J. Wysocki return ret; 1159f721889fSRafael J. Wysocki } 116085168d56SUlf Hansson 116185168d56SUlf Hansson /** 116285168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 116385168d56SUlf Hansson * @genpd: PM domain to remove the device from. 116485168d56SUlf Hansson * @dev: Device to be removed. 116585168d56SUlf Hansson */ 116685168d56SUlf Hansson int pm_genpd_remove_device(struct generic_pm_domain *genpd, 116785168d56SUlf Hansson struct device *dev) 116885168d56SUlf Hansson { 116985168d56SUlf Hansson if (!genpd || genpd != genpd_lookup_dev(dev)) 117085168d56SUlf Hansson return -EINVAL; 117185168d56SUlf Hansson 117285168d56SUlf Hansson return genpd_remove_device(genpd, dev); 117385168d56SUlf Hansson } 117424c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1175f721889fSRafael J. Wysocki 117619efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1177bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1178f721889fSRafael J. Wysocki { 11792547923dSLina Iyer struct gpd_link *link, *itr; 1180f721889fSRafael J. Wysocki int ret = 0; 1181f721889fSRafael J. Wysocki 1182fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1183fb7268beSRafael J. Wysocki || genpd == subdomain) 1184f721889fSRafael J. Wysocki return -EINVAL; 1185f721889fSRafael J. Wysocki 11862547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 11872547923dSLina Iyer if (!link) 11882547923dSLina Iyer return -ENOMEM; 11892547923dSLina Iyer 1190cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1191cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1192f721889fSRafael J. Wysocki 119317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1194bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1195f721889fSRafael J. Wysocki ret = -EINVAL; 1196f721889fSRafael J. Wysocki goto out; 1197f721889fSRafael J. Wysocki } 1198f721889fSRafael J. Wysocki 11992547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 12002547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1201f721889fSRafael J. Wysocki ret = -EINVAL; 1202f721889fSRafael J. Wysocki goto out; 1203f721889fSRafael J. Wysocki } 1204f721889fSRafael J. Wysocki } 1205f721889fSRafael J. Wysocki 12065063ce15SRafael J. Wysocki link->master = genpd; 12075063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1208bc0403ffSRafael J. Wysocki link->slave = subdomain; 1209bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1210bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1211c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1212f721889fSRafael J. Wysocki 1213f721889fSRafael J. Wysocki out: 1214ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1215cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 12162547923dSLina Iyer if (ret) 12172547923dSLina Iyer kfree(link); 1218f721889fSRafael J. Wysocki return ret; 1219f721889fSRafael J. Wysocki } 122019efa5ffSJon Hunter 122119efa5ffSJon Hunter /** 122219efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 122319efa5ffSJon Hunter * @genpd: Master PM domain to add the subdomain to. 122419efa5ffSJon Hunter * @subdomain: Subdomain to be added. 122519efa5ffSJon Hunter */ 122619efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 122719efa5ffSJon Hunter struct generic_pm_domain *subdomain) 122819efa5ffSJon Hunter { 122919efa5ffSJon Hunter int ret; 123019efa5ffSJon Hunter 123119efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 123219efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 123319efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 123419efa5ffSJon Hunter 123519efa5ffSJon Hunter return ret; 123619efa5ffSJon Hunter } 1237d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1238f721889fSRafael J. Wysocki 1239f721889fSRafael J. Wysocki /** 1240f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1241f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 12425063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1243f721889fSRafael J. Wysocki */ 1244f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 12455063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1246f721889fSRafael J. Wysocki { 12475063ce15SRafael J. Wysocki struct gpd_link *link; 1248f721889fSRafael J. Wysocki int ret = -EINVAL; 1249f721889fSRafael J. Wysocki 12505063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1251f721889fSRafael J. Wysocki return -EINVAL; 1252f721889fSRafael J. Wysocki 1253cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1254cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1255f721889fSRafael J. Wysocki 1256beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 125730e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 125830e7a65bSJon Hunter subdomain->name); 125930e7a65bSJon Hunter ret = -EBUSY; 126030e7a65bSJon Hunter goto out; 126130e7a65bSJon Hunter } 126230e7a65bSJon Hunter 12635063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 12645063ce15SRafael J. Wysocki if (link->slave != subdomain) 1265f721889fSRafael J. Wysocki continue; 1266f721889fSRafael J. Wysocki 12675063ce15SRafael J. Wysocki list_del(&link->master_node); 12685063ce15SRafael J. Wysocki list_del(&link->slave_node); 12695063ce15SRafael J. Wysocki kfree(link); 127017b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1271f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1272f721889fSRafael J. Wysocki 1273f721889fSRafael J. Wysocki ret = 0; 1274f721889fSRafael J. Wysocki break; 1275f721889fSRafael J. Wysocki } 1276f721889fSRafael J. Wysocki 127730e7a65bSJon Hunter out: 1278ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1279cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 1280f721889fSRafael J. Wysocki 1281f721889fSRafael J. Wysocki return ret; 1282f721889fSRafael J. Wysocki } 1283d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1284f721889fSRafael J. Wysocki 1285d23b9b00SRafael J. Wysocki /** 1286f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1287f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1288f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1289f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 12907eb231c3SUlf Hansson * 12917eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1292f721889fSRafael J. Wysocki */ 12937eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1294f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1295f721889fSRafael J. Wysocki { 1296f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 12977eb231c3SUlf Hansson return -EINVAL; 1298f721889fSRafael J. Wysocki 12995063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 13005063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1301f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1302f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1303f721889fSRafael J. Wysocki genpd->gov = gov; 1304f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1305c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 130617b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1307596ba34bSRafael J. Wysocki genpd->device_count = 0; 1308221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 13096ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1310de0aa06dSJon Hunter genpd->provider = NULL; 1311de0aa06dSJon Hunter genpd->has_provider = false; 1312795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1313795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 1314596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1315596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1316596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 1317596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1318596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 1319d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1320596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 1321596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1322c11f6f5bSUlf Hansson 1323c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1324c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1325c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1326c11f6f5bSUlf Hansson } 1327c11f6f5bSUlf Hansson 1328fc5cbf0cSAxel Haslam if (genpd->state_idx >= GENPD_MAX_NUM_STATES) { 1329fc5cbf0cSAxel Haslam pr_warn("Initial state index out of bounds.\n"); 1330fc5cbf0cSAxel Haslam genpd->state_idx = GENPD_MAX_NUM_STATES - 1; 1331fc5cbf0cSAxel Haslam } 1332fc5cbf0cSAxel Haslam 1333fc5cbf0cSAxel Haslam if (genpd->state_count > GENPD_MAX_NUM_STATES) { 1334fc5cbf0cSAxel Haslam pr_warn("Limiting states to %d\n", GENPD_MAX_NUM_STATES); 1335fc5cbf0cSAxel Haslam genpd->state_count = GENPD_MAX_NUM_STATES; 1336fc5cbf0cSAxel Haslam } 1337fc5cbf0cSAxel Haslam 1338fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 133990e63452SAxel Haslam if (genpd->state_count == 0) 1340fc5cbf0cSAxel Haslam genpd->state_count = 1; 1341fc5cbf0cSAxel Haslam 13425125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 13435125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 13445125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 13457eb231c3SUlf Hansson 13467eb231c3SUlf Hansson return 0; 13475125bbf3SRafael J. Wysocki } 1348be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1349aa42240aSTomasz Figa 13503fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 13513fe57710SJon Hunter { 13523fe57710SJon Hunter struct gpd_link *l, *link; 13533fe57710SJon Hunter 13543fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 13553fe57710SJon Hunter return -EINVAL; 13563fe57710SJon Hunter 13573fe57710SJon Hunter mutex_lock(&genpd->lock); 13583fe57710SJon Hunter 13593fe57710SJon Hunter if (genpd->has_provider) { 13603fe57710SJon Hunter mutex_unlock(&genpd->lock); 13613fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 13623fe57710SJon Hunter return -EBUSY; 13633fe57710SJon Hunter } 13643fe57710SJon Hunter 13653fe57710SJon Hunter if (!list_empty(&genpd->master_links) || genpd->device_count) { 13663fe57710SJon Hunter mutex_unlock(&genpd->lock); 13673fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 13683fe57710SJon Hunter return -EBUSY; 13693fe57710SJon Hunter } 13703fe57710SJon Hunter 13713fe57710SJon Hunter list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { 13723fe57710SJon Hunter list_del(&link->master_node); 13733fe57710SJon Hunter list_del(&link->slave_node); 13743fe57710SJon Hunter kfree(link); 13753fe57710SJon Hunter } 13763fe57710SJon Hunter 13773fe57710SJon Hunter list_del(&genpd->gpd_list_node); 13783fe57710SJon Hunter mutex_unlock(&genpd->lock); 13793fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 13803fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 13813fe57710SJon Hunter 13823fe57710SJon Hunter return 0; 13833fe57710SJon Hunter } 13843fe57710SJon Hunter 13853fe57710SJon Hunter /** 13863fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 13873fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 13883fe57710SJon Hunter * 13893fe57710SJon Hunter * To remove the PM domain, this function: 13903fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 13913fe57710SJon Hunter * if it was added. 13923fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 13933fe57710SJon Hunter * 13943fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 13953fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 13963fe57710SJon Hunter * devices associated with it. 13973fe57710SJon Hunter */ 13983fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 13993fe57710SJon Hunter { 14003fe57710SJon Hunter int ret; 14013fe57710SJon Hunter 14023fe57710SJon Hunter mutex_lock(&gpd_list_lock); 14033fe57710SJon Hunter ret = genpd_remove(genpd); 14043fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 14053fe57710SJon Hunter 14063fe57710SJon Hunter return ret; 14073fe57710SJon Hunter } 14083fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 14093fe57710SJon Hunter 1410aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1411892ebdccSJon Hunter 1412892ebdccSJon Hunter typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, 1413892ebdccSJon Hunter void *data); 1414892ebdccSJon Hunter 1415aa42240aSTomasz Figa /* 1416aa42240aSTomasz Figa * Device Tree based PM domain providers. 1417aa42240aSTomasz Figa * 1418aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1419aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1420aa42240aSTomasz Figa * 1421aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1422aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1423aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1424aa42240aSTomasz Figa * 1425aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1426892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1427892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 1428aa42240aSTomasz Figa * index. 1429aa42240aSTomasz Figa */ 1430aa42240aSTomasz Figa 1431aa42240aSTomasz Figa /** 1432aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1433aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1434aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1435aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1436aa42240aSTomasz Figa * into a PM domain. 1437aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1438aa42240aSTomasz Figa */ 1439aa42240aSTomasz Figa struct of_genpd_provider { 1440aa42240aSTomasz Figa struct list_head link; 1441aa42240aSTomasz Figa struct device_node *node; 1442aa42240aSTomasz Figa genpd_xlate_t xlate; 1443aa42240aSTomasz Figa void *data; 1444aa42240aSTomasz Figa }; 1445aa42240aSTomasz Figa 1446aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1447aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1448aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1449aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1450aa42240aSTomasz Figa 1451aa42240aSTomasz Figa /** 1452892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 1453aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1454aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1455aa42240aSTomasz Figa * 1456aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1457aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1458aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1459aa42240aSTomasz Figa */ 1460892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 1461aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1462aa42240aSTomasz Figa void *data) 1463aa42240aSTomasz Figa { 1464aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1465aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1466aa42240aSTomasz Figa return data; 1467aa42240aSTomasz Figa } 1468aa42240aSTomasz Figa 1469aa42240aSTomasz Figa /** 1470892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 1471aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1472aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1473aa42240aSTomasz Figa * 1474aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1475aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1476aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1477aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1478aa42240aSTomasz Figa */ 1479892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 1480aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1481aa42240aSTomasz Figa void *data) 1482aa42240aSTomasz Figa { 1483aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1484aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1485aa42240aSTomasz Figa 1486aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1487aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1488aa42240aSTomasz Figa 1489aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1490aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1491aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1492aa42240aSTomasz Figa } 1493aa42240aSTomasz Figa 1494aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1495aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1496aa42240aSTomasz Figa 1497aa42240aSTomasz Figa return genpd_data->domains[idx]; 1498aa42240aSTomasz Figa } 1499aa42240aSTomasz Figa 1500aa42240aSTomasz Figa /** 1501892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 1502aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1503aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1504aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1505aa42240aSTomasz Figa */ 1506892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1507aa42240aSTomasz Figa void *data) 1508aa42240aSTomasz Figa { 1509aa42240aSTomasz Figa struct of_genpd_provider *cp; 1510aa42240aSTomasz Figa 1511aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1512aa42240aSTomasz Figa if (!cp) 1513aa42240aSTomasz Figa return -ENOMEM; 1514aa42240aSTomasz Figa 1515aa42240aSTomasz Figa cp->node = of_node_get(np); 1516aa42240aSTomasz Figa cp->data = data; 1517aa42240aSTomasz Figa cp->xlate = xlate; 1518aa42240aSTomasz Figa 1519aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1520aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1521aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1522aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1523aa42240aSTomasz Figa 1524aa42240aSTomasz Figa return 0; 1525aa42240aSTomasz Figa } 1526892ebdccSJon Hunter 1527892ebdccSJon Hunter /** 1528892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 1529892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1530892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 1531892ebdccSJon Hunter */ 1532892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 1533892ebdccSJon Hunter struct generic_pm_domain *genpd) 1534892ebdccSJon Hunter { 15350159ec67SJon Hunter int ret = -EINVAL; 15360159ec67SJon Hunter 15370159ec67SJon Hunter if (!np || !genpd) 15380159ec67SJon Hunter return -EINVAL; 15390159ec67SJon Hunter 15400159ec67SJon Hunter mutex_lock(&gpd_list_lock); 15410159ec67SJon Hunter 15420159ec67SJon Hunter if (pm_genpd_present(genpd)) 15430159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 15440159ec67SJon Hunter 1545de0aa06dSJon Hunter if (!ret) { 1546de0aa06dSJon Hunter genpd->provider = &np->fwnode; 1547de0aa06dSJon Hunter genpd->has_provider = true; 1548de0aa06dSJon Hunter } 1549de0aa06dSJon Hunter 15500159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 15510159ec67SJon Hunter 15520159ec67SJon Hunter return ret; 1553892ebdccSJon Hunter } 1554892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 1555892ebdccSJon Hunter 1556892ebdccSJon Hunter /** 1557892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 1558892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1559892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 1560892ebdccSJon Hunter */ 1561892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 1562892ebdccSJon Hunter struct genpd_onecell_data *data) 1563892ebdccSJon Hunter { 15640159ec67SJon Hunter unsigned int i; 1565de0aa06dSJon Hunter int ret = -EINVAL; 15660159ec67SJon Hunter 15670159ec67SJon Hunter if (!np || !data) 15680159ec67SJon Hunter return -EINVAL; 15690159ec67SJon Hunter 15700159ec67SJon Hunter mutex_lock(&gpd_list_lock); 15710159ec67SJon Hunter 15720159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 1573609bed67STomeu Vizoso if (!data->domains[i]) 1574609bed67STomeu Vizoso continue; 1575de0aa06dSJon Hunter if (!pm_genpd_present(data->domains[i])) 1576de0aa06dSJon Hunter goto error; 1577de0aa06dSJon Hunter 1578de0aa06dSJon Hunter data->domains[i]->provider = &np->fwnode; 1579de0aa06dSJon Hunter data->domains[i]->has_provider = true; 15800159ec67SJon Hunter } 15810159ec67SJon Hunter 15820159ec67SJon Hunter ret = genpd_add_provider(np, genpd_xlate_onecell, data); 1583de0aa06dSJon Hunter if (ret < 0) 1584de0aa06dSJon Hunter goto error; 1585de0aa06dSJon Hunter 1586de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1587de0aa06dSJon Hunter 1588de0aa06dSJon Hunter return 0; 1589de0aa06dSJon Hunter 1590de0aa06dSJon Hunter error: 1591de0aa06dSJon Hunter while (i--) { 1592609bed67STomeu Vizoso if (!data->domains[i]) 1593609bed67STomeu Vizoso continue; 1594de0aa06dSJon Hunter data->domains[i]->provider = NULL; 1595de0aa06dSJon Hunter data->domains[i]->has_provider = false; 1596de0aa06dSJon Hunter } 15970159ec67SJon Hunter 15980159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 15990159ec67SJon Hunter 16000159ec67SJon Hunter return ret; 1601892ebdccSJon Hunter } 1602892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 1603aa42240aSTomasz Figa 1604aa42240aSTomasz Figa /** 1605aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1606aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1607aa42240aSTomasz Figa */ 1608aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1609aa42240aSTomasz Figa { 1610aa42240aSTomasz Figa struct of_genpd_provider *cp; 1611de0aa06dSJon Hunter struct generic_pm_domain *gpd; 1612aa42240aSTomasz Figa 1613de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 1614aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1615aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1616aa42240aSTomasz Figa if (cp->node == np) { 1617de0aa06dSJon Hunter /* 1618de0aa06dSJon Hunter * For each PM domain associated with the 1619de0aa06dSJon Hunter * provider, set the 'has_provider' to false 1620de0aa06dSJon Hunter * so that the PM domain can be safely removed. 1621de0aa06dSJon Hunter */ 1622de0aa06dSJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) 1623de0aa06dSJon Hunter if (gpd->provider == &np->fwnode) 1624de0aa06dSJon Hunter gpd->has_provider = false; 1625de0aa06dSJon Hunter 1626aa42240aSTomasz Figa list_del(&cp->link); 1627aa42240aSTomasz Figa of_node_put(cp->node); 1628aa42240aSTomasz Figa kfree(cp); 1629aa42240aSTomasz Figa break; 1630aa42240aSTomasz Figa } 1631aa42240aSTomasz Figa } 1632aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1633de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 1634aa42240aSTomasz Figa } 1635aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1636aa42240aSTomasz Figa 1637aa42240aSTomasz Figa /** 1638f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 1639aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1640aa42240aSTomasz Figa * 1641aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1642aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1643aa42240aSTomasz Figa * domain. 1644aa42240aSTomasz Figa * 1645aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1646aa42240aSTomasz Figa * on failure. 1647aa42240aSTomasz Figa */ 1648f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 1649aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1650aa42240aSTomasz Figa { 1651aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1652aa42240aSTomasz Figa struct of_genpd_provider *provider; 1653aa42240aSTomasz Figa 165441795a8aSJon Hunter if (!genpdspec) 165541795a8aSJon Hunter return ERR_PTR(-EINVAL); 165641795a8aSJon Hunter 1657aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1658aa42240aSTomasz Figa 1659aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1660aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1661aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1662aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1663aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1664aa42240aSTomasz Figa break; 1665aa42240aSTomasz Figa } 1666aa42240aSTomasz Figa 1667aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1668aa42240aSTomasz Figa 1669aa42240aSTomasz Figa return genpd; 1670aa42240aSTomasz Figa } 1671aa42240aSTomasz Figa 1672aa42240aSTomasz Figa /** 1673ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 1674ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 1675ec69572bSJon Hunter * @dev: Device to be added. 1676ec69572bSJon Hunter * 1677ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 1678ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 1679ec69572bSJon Hunter */ 1680ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 1681ec69572bSJon Hunter { 1682ec69572bSJon Hunter struct generic_pm_domain *genpd; 168319efa5ffSJon Hunter int ret; 168419efa5ffSJon Hunter 168519efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1686ec69572bSJon Hunter 1687f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 168819efa5ffSJon Hunter if (IS_ERR(genpd)) { 168919efa5ffSJon Hunter ret = PTR_ERR(genpd); 169019efa5ffSJon Hunter goto out; 169119efa5ffSJon Hunter } 1692ec69572bSJon Hunter 169319efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, NULL); 169419efa5ffSJon Hunter 169519efa5ffSJon Hunter out: 169619efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 169719efa5ffSJon Hunter 169819efa5ffSJon Hunter return ret; 1699ec69572bSJon Hunter } 1700ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 1701ec69572bSJon Hunter 1702ec69572bSJon Hunter /** 1703ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1704ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 1705ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 1706ec69572bSJon Hunter * 1707ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 1708ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 1709ec69572bSJon Hunter * negative error code on failure. 1710ec69572bSJon Hunter */ 1711ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 1712ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 1713ec69572bSJon Hunter { 1714ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 171519efa5ffSJon Hunter int ret; 171619efa5ffSJon Hunter 171719efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1718ec69572bSJon Hunter 1719f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 172019efa5ffSJon Hunter if (IS_ERR(parent)) { 172119efa5ffSJon Hunter ret = PTR_ERR(parent); 172219efa5ffSJon Hunter goto out; 172319efa5ffSJon Hunter } 1724ec69572bSJon Hunter 1725f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 172619efa5ffSJon Hunter if (IS_ERR(subdomain)) { 172719efa5ffSJon Hunter ret = PTR_ERR(subdomain); 172819efa5ffSJon Hunter goto out; 172919efa5ffSJon Hunter } 1730ec69572bSJon Hunter 173119efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 173219efa5ffSJon Hunter 173319efa5ffSJon Hunter out: 173419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 173519efa5ffSJon Hunter 173619efa5ffSJon Hunter return ret; 1737ec69572bSJon Hunter } 1738ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 1739ec69572bSJon Hunter 1740ec69572bSJon Hunter /** 174117926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 174217926551SJon Hunter * @provider: Pointer to device structure associated with provider 174317926551SJon Hunter * 174417926551SJon Hunter * Find the last PM domain that was added by a particular provider and 174517926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 174617926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 174717926551SJon Hunter * domain will only be removed, if the provider associated with domain 174817926551SJon Hunter * has been removed. 174917926551SJon Hunter * 175017926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 175117926551SJon Hunter * ERR_PTR() on failure. 175217926551SJon Hunter */ 175317926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 175417926551SJon Hunter { 175517926551SJon Hunter struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT); 175617926551SJon Hunter int ret; 175717926551SJon Hunter 175817926551SJon Hunter if (IS_ERR_OR_NULL(np)) 175917926551SJon Hunter return ERR_PTR(-EINVAL); 176017926551SJon Hunter 176117926551SJon Hunter mutex_lock(&gpd_list_lock); 176217926551SJon Hunter list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 176317926551SJon Hunter if (gpd->provider == &np->fwnode) { 176417926551SJon Hunter ret = genpd_remove(gpd); 176517926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 176617926551SJon Hunter break; 176717926551SJon Hunter } 176817926551SJon Hunter } 176917926551SJon Hunter mutex_unlock(&gpd_list_lock); 177017926551SJon Hunter 177117926551SJon Hunter return genpd; 177217926551SJon Hunter } 177317926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 177417926551SJon Hunter 177517926551SJon Hunter /** 1776aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 17778bb6944eSJon Hunter * @dev: Device to detach. 1778aa42240aSTomasz Figa * @power_off: Currently not used 1779aa42240aSTomasz Figa * 1780aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1781aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1782aa42240aSTomasz Figa */ 1783aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1784aa42240aSTomasz Figa { 1785446d999cSRussell King struct generic_pm_domain *pd; 178693af5e93SGeert Uytterhoeven unsigned int i; 1787aa42240aSTomasz Figa int ret = 0; 1788aa42240aSTomasz Figa 178985168d56SUlf Hansson pd = dev_to_genpd(dev); 179085168d56SUlf Hansson if (IS_ERR(pd)) 1791aa42240aSTomasz Figa return; 1792aa42240aSTomasz Figa 1793aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1794aa42240aSTomasz Figa 179593af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 179685168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 1797aa42240aSTomasz Figa if (ret != -EAGAIN) 1798aa42240aSTomasz Figa break; 179993af5e93SGeert Uytterhoeven 180093af5e93SGeert Uytterhoeven mdelay(i); 1801aa42240aSTomasz Figa cond_resched(); 1802aa42240aSTomasz Figa } 1803aa42240aSTomasz Figa 1804aa42240aSTomasz Figa if (ret < 0) { 1805aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1806aa42240aSTomasz Figa pd->name, ret); 1807aa42240aSTomasz Figa return; 1808aa42240aSTomasz Figa } 1809aa42240aSTomasz Figa 1810aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1811aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1812aa42240aSTomasz Figa } 1813aa42240aSTomasz Figa 1814632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1815632f7ce3SRussell King { 1816632f7ce3SRussell King struct generic_pm_domain *pd; 1817632f7ce3SRussell King 1818632f7ce3SRussell King pd = dev_to_genpd(dev); 1819632f7ce3SRussell King if (IS_ERR(pd)) 1820632f7ce3SRussell King return; 1821632f7ce3SRussell King 1822632f7ce3SRussell King genpd_queue_power_off_work(pd); 1823632f7ce3SRussell King } 1824632f7ce3SRussell King 1825aa42240aSTomasz Figa /** 1826aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1827aa42240aSTomasz Figa * @dev: Device to attach. 1828aa42240aSTomasz Figa * 1829aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1830aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1831aa42240aSTomasz Figa * 1832aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1833aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1834aa42240aSTomasz Figa * 1835311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1836311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1837311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1838311fa6adSJon Hunter * probed and to re-try again later. 1839aa42240aSTomasz Figa */ 1840aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 1841aa42240aSTomasz Figa { 1842aa42240aSTomasz Figa struct of_phandle_args pd_args; 1843aa42240aSTomasz Figa struct generic_pm_domain *pd; 184493af5e93SGeert Uytterhoeven unsigned int i; 1845aa42240aSTomasz Figa int ret; 1846aa42240aSTomasz Figa 1847aa42240aSTomasz Figa if (!dev->of_node) 1848aa42240aSTomasz Figa return -ENODEV; 1849aa42240aSTomasz Figa 1850aa42240aSTomasz Figa if (dev->pm_domain) 1851aa42240aSTomasz Figa return -EEXIST; 1852aa42240aSTomasz Figa 1853aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 1854aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 1855aa42240aSTomasz Figa if (ret < 0) { 1856aa42240aSTomasz Figa if (ret != -ENOENT) 1857aa42240aSTomasz Figa return ret; 1858aa42240aSTomasz Figa 1859aa42240aSTomasz Figa /* 1860aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 1861aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 1862aa42240aSTomasz Figa */ 1863aa42240aSTomasz Figa pd_args.args_count = 0; 1864aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 1865aa42240aSTomasz Figa "samsung,power-domain", 0); 1866aa42240aSTomasz Figa if (!pd_args.np) 1867aa42240aSTomasz Figa return -ENOENT; 1868aa42240aSTomasz Figa } 1869aa42240aSTomasz Figa 187019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1871f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 1872265e2cf6SEric Anholt of_node_put(pd_args.np); 1873aa42240aSTomasz Figa if (IS_ERR(pd)) { 187419efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 1875aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 1876aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 1877311fa6adSJon Hunter return -EPROBE_DEFER; 1878aa42240aSTomasz Figa } 1879aa42240aSTomasz Figa 1880aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 1881aa42240aSTomasz Figa 188293af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 188319efa5ffSJon Hunter ret = genpd_add_device(pd, dev, NULL); 1884aa42240aSTomasz Figa if (ret != -EAGAIN) 1885aa42240aSTomasz Figa break; 188693af5e93SGeert Uytterhoeven 188793af5e93SGeert Uytterhoeven mdelay(i); 1888aa42240aSTomasz Figa cond_resched(); 1889aa42240aSTomasz Figa } 189019efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 1891aa42240aSTomasz Figa 1892aa42240aSTomasz Figa if (ret < 0) { 1893aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 1894aa42240aSTomasz Figa pd->name, ret); 1895311fa6adSJon Hunter goto out; 1896aa42240aSTomasz Figa } 1897aa42240aSTomasz Figa 1898aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 1899632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 1900aa42240aSTomasz Figa 190153af16f7SUlf Hansson mutex_lock(&pd->lock); 190253af16f7SUlf Hansson ret = genpd_poweron(pd, 0); 190353af16f7SUlf Hansson mutex_unlock(&pd->lock); 1904311fa6adSJon Hunter out: 1905311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 1906aa42240aSTomasz Figa } 1907aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 1908d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 19092bd5306aSMaciej Matraszek 19102bd5306aSMaciej Matraszek 19112bd5306aSMaciej Matraszek /*** debugfs support ***/ 19122bd5306aSMaciej Matraszek 19138b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 19142bd5306aSMaciej Matraszek #include <linux/pm.h> 19152bd5306aSMaciej Matraszek #include <linux/device.h> 19162bd5306aSMaciej Matraszek #include <linux/debugfs.h> 19172bd5306aSMaciej Matraszek #include <linux/seq_file.h> 19182bd5306aSMaciej Matraszek #include <linux/init.h> 19192bd5306aSMaciej Matraszek #include <linux/kobject.h> 19202bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 19212bd5306aSMaciej Matraszek 19222bd5306aSMaciej Matraszek /* 19232bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 1924d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 19252bd5306aSMaciej Matraszek */ 19262bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 19272bd5306aSMaciej Matraszek { 19282bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 19292bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 19302bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 19312bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 19322bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 19332bd5306aSMaciej Matraszek }; 19342bd5306aSMaciej Matraszek const char *p = ""; 19352bd5306aSMaciej Matraszek 19362bd5306aSMaciej Matraszek if (dev->power.runtime_error) 19372bd5306aSMaciej Matraszek p = "error"; 19382bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 19392bd5306aSMaciej Matraszek p = "unsupported"; 19402bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 19412bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 19422bd5306aSMaciej Matraszek else 19432bd5306aSMaciej Matraszek WARN_ON(1); 19442bd5306aSMaciej Matraszek 19452bd5306aSMaciej Matraszek seq_puts(s, p); 19462bd5306aSMaciej Matraszek } 19472bd5306aSMaciej Matraszek 19482bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 194966a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 19502bd5306aSMaciej Matraszek { 19512bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 19522bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 19532bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 19542bd5306aSMaciej Matraszek }; 19552bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 19562bd5306aSMaciej Matraszek const char *kobj_path; 19572bd5306aSMaciej Matraszek struct gpd_link *link; 19586954d432SGeert Uytterhoeven char state[16]; 19592bd5306aSMaciej Matraszek int ret; 19602bd5306aSMaciej Matraszek 196166a5ca4bSKevin Hilman ret = mutex_lock_interruptible(&genpd->lock); 19622bd5306aSMaciej Matraszek if (ret) 19632bd5306aSMaciej Matraszek return -ERESTARTSYS; 19642bd5306aSMaciej Matraszek 196566a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 19662bd5306aSMaciej Matraszek goto exit; 1967fc5cbf0cSAxel Haslam if (genpd->status == GPD_STATE_POWER_OFF) 19680ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 19696954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 1970fc5cbf0cSAxel Haslam else 19716954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 19726954d432SGeert Uytterhoeven status_lookup[genpd->status]); 19736954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 19742bd5306aSMaciej Matraszek 19752bd5306aSMaciej Matraszek /* 19762bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 19772bd5306aSMaciej Matraszek * master and slave, so we are safe. 197866a5ca4bSKevin Hilman * Also genpd->name is immutable. 19792bd5306aSMaciej Matraszek */ 198066a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 19812bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 198266a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 19832bd5306aSMaciej Matraszek seq_puts(s, ", "); 19842bd5306aSMaciej Matraszek } 19852bd5306aSMaciej Matraszek 198666a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 19872bd5306aSMaciej Matraszek kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL); 19882bd5306aSMaciej Matraszek if (kobj_path == NULL) 19892bd5306aSMaciej Matraszek continue; 19902bd5306aSMaciej Matraszek 19912bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 19922bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 19932bd5306aSMaciej Matraszek kfree(kobj_path); 19942bd5306aSMaciej Matraszek } 19952bd5306aSMaciej Matraszek 19962bd5306aSMaciej Matraszek seq_puts(s, "\n"); 19972bd5306aSMaciej Matraszek exit: 199866a5ca4bSKevin Hilman mutex_unlock(&genpd->lock); 19992bd5306aSMaciej Matraszek 20002bd5306aSMaciej Matraszek return 0; 20012bd5306aSMaciej Matraszek } 20022bd5306aSMaciej Matraszek 20032bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 20042bd5306aSMaciej Matraszek { 200566a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 20062bd5306aSMaciej Matraszek int ret = 0; 20072bd5306aSMaciej Matraszek 20082bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 20092bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 20102bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 20112bd5306aSMaciej Matraszek 20122bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 20132bd5306aSMaciej Matraszek if (ret) 20142bd5306aSMaciej Matraszek return -ERESTARTSYS; 20152bd5306aSMaciej Matraszek 201666a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 201766a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 20182bd5306aSMaciej Matraszek if (ret) 20192bd5306aSMaciej Matraszek break; 20202bd5306aSMaciej Matraszek } 20212bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 20222bd5306aSMaciej Matraszek 20232bd5306aSMaciej Matraszek return ret; 20242bd5306aSMaciej Matraszek } 20252bd5306aSMaciej Matraszek 20262bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 20272bd5306aSMaciej Matraszek { 20282bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 20292bd5306aSMaciej Matraszek } 20302bd5306aSMaciej Matraszek 20312bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 20322bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 20332bd5306aSMaciej Matraszek .read = seq_read, 20342bd5306aSMaciej Matraszek .llseek = seq_lseek, 20352bd5306aSMaciej Matraszek .release = single_release, 20362bd5306aSMaciej Matraszek }; 20372bd5306aSMaciej Matraszek 20382bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 20392bd5306aSMaciej Matraszek { 20402bd5306aSMaciej Matraszek struct dentry *d; 20412bd5306aSMaciej Matraszek 20422bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 20432bd5306aSMaciej Matraszek 20442bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 20452bd5306aSMaciej Matraszek return -ENOMEM; 20462bd5306aSMaciej Matraszek 20472bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 20482bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 20492bd5306aSMaciej Matraszek if (!d) 20502bd5306aSMaciej Matraszek return -ENOMEM; 20512bd5306aSMaciej Matraszek 20522bd5306aSMaciej Matraszek return 0; 20532bd5306aSMaciej Matraszek } 20542bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 20552bd5306aSMaciej Matraszek 20562bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 20572bd5306aSMaciej Matraszek { 20582bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 20592bd5306aSMaciej Matraszek } 20602bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 20618b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 2062