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 2393af5e93SGeert Uytterhoeven #define GENPD_RETRY_MAX_MS 250 /* Approximate */ 2493af5e93SGeert Uytterhoeven 25d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 26d5e4cbfeSRafael J. Wysocki ({ \ 27d5e4cbfeSRafael J. Wysocki type (*__routine)(struct device *__d); \ 28d5e4cbfeSRafael J. Wysocki type __ret = (type)0; \ 29d5e4cbfeSRafael J. Wysocki \ 30d5e4cbfeSRafael J. Wysocki __routine = genpd->dev_ops.callback; \ 31d5e4cbfeSRafael J. Wysocki if (__routine) { \ 32d5e4cbfeSRafael J. Wysocki __ret = __routine(dev); \ 33d5e4cbfeSRafael J. Wysocki } \ 34d5e4cbfeSRafael J. Wysocki __ret; \ 35d5e4cbfeSRafael J. Wysocki }) 36f721889fSRafael J. Wysocki 375125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list); 385125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock); 395125bbf3SRafael J. Wysocki 40446d999cSRussell King /* 41446d999cSRussell King * Get the generic PM domain for a particular struct device. 42446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 43446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 44446d999cSRussell King * Any failure results in NULL being returned. 45446d999cSRussell King */ 46446d999cSRussell King struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) 47446d999cSRussell King { 48446d999cSRussell King struct generic_pm_domain *genpd = NULL, *gpd; 49446d999cSRussell King 50446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 51446d999cSRussell King return NULL; 52446d999cSRussell King 53446d999cSRussell King mutex_lock(&gpd_list_lock); 54446d999cSRussell King list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 55446d999cSRussell King if (&gpd->domain == dev->pm_domain) { 56446d999cSRussell King genpd = gpd; 57446d999cSRussell King break; 58446d999cSRussell King } 59446d999cSRussell King } 60446d999cSRussell King mutex_unlock(&gpd_list_lock); 61446d999cSRussell King 62446d999cSRussell King return genpd; 63446d999cSRussell King } 64446d999cSRussell King 65446d999cSRussell King /* 66446d999cSRussell King * This should only be used where we are certain that the pm_domain 67446d999cSRussell King * attached to the device is a genpd domain. 68446d999cSRussell King */ 69446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 705248051bSRafael J. Wysocki { 715248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 725248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 735248051bSRafael J. Wysocki 74596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 755248051bSRafael J. Wysocki } 76f721889fSRafael J. Wysocki 772b1d88cdSUlf Hansson static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) 78d5e4cbfeSRafael J. Wysocki { 7951cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 80d5e4cbfeSRafael J. Wysocki } 81d5e4cbfeSRafael J. Wysocki 822b1d88cdSUlf Hansson static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) 83d5e4cbfeSRafael J. Wysocki { 84ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 85d5e4cbfeSRafael J. Wysocki } 86d5e4cbfeSRafael J. Wysocki 87c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 88f721889fSRafael J. Wysocki { 89c4bb3160SRafael J. Wysocki bool ret = false; 90c4bb3160SRafael J. Wysocki 91c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 92c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 93c4bb3160SRafael J. Wysocki 94c4bb3160SRafael J. Wysocki return ret; 95c4bb3160SRafael J. Wysocki } 96c4bb3160SRafael J. Wysocki 97c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 98c4bb3160SRafael J. Wysocki { 99c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 1004e857c58SPeter Zijlstra smp_mb__after_atomic(); 101f721889fSRafael J. Wysocki } 102f721889fSRafael J. Wysocki 103a4630c61SGeert Uytterhoeven static int genpd_power_on(struct generic_pm_domain *genpd, bool timed) 104c8f0ea45SGeert Uytterhoeven { 105c8f0ea45SGeert Uytterhoeven ktime_t time_start; 106c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 107c8f0ea45SGeert Uytterhoeven int ret; 108c8f0ea45SGeert Uytterhoeven 109c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 110c8f0ea45SGeert Uytterhoeven return 0; 111c8f0ea45SGeert Uytterhoeven 112a4630c61SGeert Uytterhoeven if (!timed) 113a4630c61SGeert Uytterhoeven return genpd->power_on(genpd); 114a4630c61SGeert Uytterhoeven 115c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 116c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 117c8f0ea45SGeert Uytterhoeven if (ret) 118c8f0ea45SGeert Uytterhoeven return ret; 119c8f0ea45SGeert Uytterhoeven 120c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 121c8f0ea45SGeert Uytterhoeven if (elapsed_ns <= genpd->power_on_latency_ns) 122c8f0ea45SGeert Uytterhoeven return ret; 123c8f0ea45SGeert Uytterhoeven 124c8f0ea45SGeert Uytterhoeven genpd->power_on_latency_ns = elapsed_ns; 125c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 1266d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 127c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 128c8f0ea45SGeert Uytterhoeven 129c8f0ea45SGeert Uytterhoeven return ret; 130c8f0ea45SGeert Uytterhoeven } 131c8f0ea45SGeert Uytterhoeven 132a4630c61SGeert Uytterhoeven static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) 133c8f0ea45SGeert Uytterhoeven { 134c8f0ea45SGeert Uytterhoeven ktime_t time_start; 135c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 136c8f0ea45SGeert Uytterhoeven int ret; 137c8f0ea45SGeert Uytterhoeven 138c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 139c8f0ea45SGeert Uytterhoeven return 0; 140c8f0ea45SGeert Uytterhoeven 141a4630c61SGeert Uytterhoeven if (!timed) 142a4630c61SGeert Uytterhoeven return genpd->power_off(genpd); 143a4630c61SGeert Uytterhoeven 144c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 145c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 146c8f0ea45SGeert Uytterhoeven if (ret == -EBUSY) 147c8f0ea45SGeert Uytterhoeven return ret; 148c8f0ea45SGeert Uytterhoeven 149c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 150c8f0ea45SGeert Uytterhoeven if (elapsed_ns <= genpd->power_off_latency_ns) 151c8f0ea45SGeert Uytterhoeven return ret; 152c8f0ea45SGeert Uytterhoeven 153c8f0ea45SGeert Uytterhoeven genpd->power_off_latency_ns = elapsed_ns; 154c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 1556d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 156c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 157c8f0ea45SGeert Uytterhoeven 158c8f0ea45SGeert Uytterhoeven return ret; 159c8f0ea45SGeert Uytterhoeven } 160c8f0ea45SGeert Uytterhoeven 161f721889fSRafael J. Wysocki /** 1627420aa4fSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff(). 16329e47e21SUlf Hansson * @genpd: PM domait to power off. 16429e47e21SUlf Hansson * 1657420aa4fSUlf Hansson * Queue up the execution of genpd_poweroff() unless it's already been done 16629e47e21SUlf Hansson * before. 16729e47e21SUlf Hansson */ 16829e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 16929e47e21SUlf Hansson { 17029e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 17129e47e21SUlf Hansson } 17229e47e21SUlf Hansson 17329e47e21SUlf Hansson /** 1747420aa4fSUlf Hansson * __genpd_poweron - Restore power to a given PM domain and its masters. 1755248051bSRafael J. Wysocki * @genpd: PM domain to power up. 1760106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 1775248051bSRafael J. Wysocki * 1785063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 1795248051bSRafael J. Wysocki * resume a device belonging to it. 1805248051bSRafael J. Wysocki */ 1810106ef51SMarek Szyprowski static int __genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth) 1825248051bSRafael J. Wysocki { 1835063ce15SRafael J. Wysocki struct gpd_link *link; 1845248051bSRafael J. Wysocki int ret = 0; 1855248051bSRafael J. Wysocki 18617b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 187596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1883f241775SRafael J. Wysocki return 0; 1895248051bSRafael J. Wysocki 1905063ce15SRafael J. Wysocki /* 1915063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 1925063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 1935063ce15SRafael J. Wysocki * with it. 1945063ce15SRafael J. Wysocki */ 1955063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1960106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 1975248051bSRafael J. Wysocki 1980106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 1990106ef51SMarek Szyprowski 2000106ef51SMarek Szyprowski mutex_lock_nested(&master->lock, depth + 1); 2010106ef51SMarek Szyprowski ret = __genpd_poweron(master, depth + 1); 2020106ef51SMarek Szyprowski mutex_unlock(&master->lock); 2030106ef51SMarek Szyprowski 2045063ce15SRafael J. Wysocki if (ret) { 2050106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 2069e08cf42SRafael J. Wysocki goto err; 2075248051bSRafael J. Wysocki } 2085063ce15SRafael J. Wysocki } 2095248051bSRafael J. Wysocki 210a4630c61SGeert Uytterhoeven ret = genpd_power_on(genpd, true); 2119e08cf42SRafael J. Wysocki if (ret) 2129e08cf42SRafael J. Wysocki goto err; 2130140d8bdSRafael J. Wysocki 214ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 2153f241775SRafael J. Wysocki return 0; 2169e08cf42SRafael J. Wysocki 2179e08cf42SRafael J. Wysocki err: 21829e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 21929e47e21SUlf Hansson &genpd->slave_links, 22029e47e21SUlf Hansson slave_node) { 2215063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 22229e47e21SUlf Hansson genpd_queue_power_off_work(link->master); 22329e47e21SUlf Hansson } 2249e08cf42SRafael J. Wysocki 2253f241775SRafael J. Wysocki return ret; 2263f241775SRafael J. Wysocki } 2273f241775SRafael J. Wysocki 2283f241775SRafael J. Wysocki /** 2297420aa4fSUlf Hansson * genpd_poweron - Restore power to a given PM domain and its masters. 2303f241775SRafael J. Wysocki * @genpd: PM domain to power up. 2313f241775SRafael J. Wysocki */ 2327420aa4fSUlf Hansson static int genpd_poweron(struct generic_pm_domain *genpd) 2333f241775SRafael J. Wysocki { 2343f241775SRafael J. Wysocki int ret; 2353f241775SRafael J. Wysocki 2363f241775SRafael J. Wysocki mutex_lock(&genpd->lock); 2370106ef51SMarek Szyprowski ret = __genpd_poweron(genpd, 0); 2383f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 2393f241775SRafael J. Wysocki return ret; 2405248051bSRafael J. Wysocki } 2415248051bSRafael J. Wysocki 2420106ef51SMarek Szyprowski 2438e9afafdSRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) 2448e9afafdSRafael J. Wysocki { 2452b1d88cdSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, save_state, dev); 2468e9afafdSRafael J. Wysocki } 2478e9afafdSRafael J. Wysocki 248ba2bbfbfSUlf Hansson static int genpd_restore_dev(struct generic_pm_domain *genpd, 2492b1d88cdSUlf Hansson struct device *dev) 2508e9afafdSRafael J. Wysocki { 251ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); 2528e9afafdSRafael J. Wysocki } 2538e9afafdSRafael J. Wysocki 2546ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 2556ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 2566ff7bb0dSRafael J. Wysocki { 2576ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 2586ff7bb0dSRafael J. Wysocki struct device *dev; 2596ff7bb0dSRafael J. Wysocki 2606ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 2616ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 2626ff7bb0dSRafael J. Wysocki 2636ff7bb0dSRafael J. Wysocki for (;;) { 2646ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 2656ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 2666ff7bb0dSRafael J. Wysocki 2676ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 2686ff7bb0dSRafael J. Wysocki 2696ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 2706ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 2711d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 2726ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 2736ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 2746ff7bb0dSRafael J. Wysocki } else { 2756ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 2766ff7bb0dSRafael J. Wysocki } 2776ff7bb0dSRafael J. Wysocki 2786ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 2796ff7bb0dSRafael J. Wysocki 2806ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 2816ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 2826ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2836ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 2846ff7bb0dSRafael J. Wysocki } 2856ff7bb0dSRafael J. Wysocki 2866ff7bb0dSRafael J. Wysocki dev = dev->parent; 2876ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 2886ff7bb0dSRafael J. Wysocki break; 2896ff7bb0dSRafael J. Wysocki } 2906ff7bb0dSRafael J. Wysocki 2916ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2926ff7bb0dSRafael J. Wysocki } 2936ff7bb0dSRafael J. Wysocki 2945248051bSRafael J. Wysocki /** 2957420aa4fSUlf Hansson * genpd_poweroff - Remove power from a given PM domain. 296f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 297f96b3c4fSUlf Hansson * @is_async: PM domain is powered down from a scheduled work 298f721889fSRafael J. Wysocki * 299f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 300ba2bbfbfSUlf Hansson * have been powered down, remove power from @genpd. 301f721889fSRafael J. Wysocki */ 3027420aa4fSUlf Hansson static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) 303f721889fSRafael J. Wysocki { 3044605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 3055063ce15SRafael J. Wysocki struct gpd_link *link; 306ba2bbfbfSUlf Hansson unsigned int not_suspended = 0; 307f721889fSRafael J. Wysocki 308c6d22b37SRafael J. Wysocki /* 309c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 310c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 311ba2bbfbfSUlf Hansson * (2) System suspend is in progress. 312c6d22b37SRafael J. Wysocki */ 3133f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 314ba2bbfbfSUlf Hansson || genpd->prepared_count > 0) 315f721889fSRafael J. Wysocki return 0; 316f721889fSRafael J. Wysocki 317c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 318f721889fSRafael J. Wysocki return -EBUSY; 319f721889fSRafael J. Wysocki 32034b1f762SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) { 32134b1f762SRafael J. Wysocki enum pm_qos_flags_status stat; 32234b1f762SRafael J. Wysocki 32334b1f762SRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, 32434b1f762SRafael J. Wysocki PM_QOS_FLAG_NO_POWER_OFF 32534b1f762SRafael J. Wysocki | PM_QOS_FLAG_REMOTE_WAKEUP); 32634b1f762SRafael J. Wysocki if (stat > PM_QOS_FLAGS_NONE) 32734b1f762SRafael J. Wysocki return -EBUSY; 32834b1f762SRafael J. Wysocki 329298cd0f0SLina Iyer if (!pm_runtime_suspended(pdd->dev) || pdd->dev->power.irq_safe) 330f721889fSRafael J. Wysocki not_suspended++; 33134b1f762SRafael J. Wysocki } 332f721889fSRafael J. Wysocki 333f96b3c4fSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && is_async)) 334f721889fSRafael J. Wysocki return -EBUSY; 335f721889fSRafael J. Wysocki 336f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 337f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 338f721889fSRafael J. Wysocki return -EAGAIN; 339f721889fSRafael J. Wysocki } 340f721889fSRafael J. Wysocki 3413c07cbc4SRafael J. Wysocki if (genpd->power_off) { 342ba2bbfbfSUlf Hansson int ret; 343ba2bbfbfSUlf Hansson 344ba2bbfbfSUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 345ba2bbfbfSUlf Hansson return -EBUSY; 34617b75ecaSRafael J. Wysocki 3473c07cbc4SRafael J. Wysocki /* 3485063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 3497420aa4fSUlf Hansson * managed to call genpd_poweron() for the master yet after 3507420aa4fSUlf Hansson * incrementing it. In that case genpd_poweron() will wait 3513c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 3527420aa4fSUlf Hansson * the genpd_poweron() restore power for us (this shouldn't 3533c07cbc4SRafael J. Wysocki * happen very often). 3543c07cbc4SRafael J. Wysocki */ 355a4630c61SGeert Uytterhoeven ret = genpd_power_off(genpd, true); 356ba2bbfbfSUlf Hansson if (ret) 357ba2bbfbfSUlf Hansson return ret; 358d2805402SRafael J. Wysocki } 359f721889fSRafael J. Wysocki 36017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 361221e9b58SRafael J. Wysocki 3625063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 3635063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 3645063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 3655063ce15SRafael J. Wysocki } 36617b75ecaSRafael J. Wysocki 367ba2bbfbfSUlf Hansson return 0; 368f721889fSRafael J. Wysocki } 369f721889fSRafael J. Wysocki 370f721889fSRafael J. Wysocki /** 371f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 372f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 373f721889fSRafael J. Wysocki */ 374f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 375f721889fSRafael J. Wysocki { 376f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 377f721889fSRafael J. Wysocki 378f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 379f721889fSRafael J. Wysocki 380ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 3817420aa4fSUlf Hansson genpd_poweroff(genpd, true); 382ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 383f721889fSRafael J. Wysocki } 384f721889fSRafael J. Wysocki 385f721889fSRafael J. Wysocki /** 386f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 387f721889fSRafael J. Wysocki * @dev: Device to suspend. 388f721889fSRafael J. Wysocki * 389f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 390f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 391f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 392f721889fSRafael J. Wysocki */ 393f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 394f721889fSRafael J. Wysocki { 395f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 396b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 3972b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 398ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 3992b1d88cdSUlf Hansson ktime_t time_start; 4002b1d88cdSUlf Hansson s64 elapsed_ns; 401d5e4cbfeSRafael J. Wysocki int ret; 402f721889fSRafael J. Wysocki 403f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 404f721889fSRafael J. Wysocki 4055248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4065248051bSRafael J. Wysocki if (IS_ERR(genpd)) 407f721889fSRafael J. Wysocki return -EINVAL; 408f721889fSRafael J. Wysocki 409ffe12855SUlf Hansson /* 410ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 411ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 412ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 413ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 414ffe12855SUlf Hansson */ 415b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 416ffe12855SUlf Hansson if (runtime_pm && stop_ok && !stop_ok(dev)) 417b02c999aSRafael J. Wysocki return -EBUSY; 418b02c999aSRafael J. Wysocki 4192b1d88cdSUlf Hansson /* Measure suspend latency. */ 420ffe12855SUlf Hansson if (runtime_pm) 4212b1d88cdSUlf Hansson time_start = ktime_get(); 4222b1d88cdSUlf Hansson 423ba2bbfbfSUlf Hansson ret = genpd_save_dev(genpd, dev); 424f721889fSRafael J. Wysocki if (ret) 42517b75ecaSRafael J. Wysocki return ret; 42617b75ecaSRafael J. Wysocki 4272b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 428ba2bbfbfSUlf Hansson if (ret) { 4292b1d88cdSUlf Hansson genpd_restore_dev(genpd, dev); 430ba2bbfbfSUlf Hansson return ret; 431ba2bbfbfSUlf Hansson } 432ba2bbfbfSUlf Hansson 4332b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 434ffe12855SUlf Hansson if (runtime_pm) { 4352b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 4362b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 4372b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 4382b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 4392b1d88cdSUlf Hansson elapsed_ns); 4402b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 4412b1d88cdSUlf Hansson td->constraint_changed = true; 4422b1d88cdSUlf Hansson } 443ffe12855SUlf Hansson } 4442b1d88cdSUlf Hansson 4450aa2a221SRafael J. Wysocki /* 4460aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 4470aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 4480aa2a221SRafael J. Wysocki */ 4490aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 4500aa2a221SRafael J. Wysocki return 0; 4510aa2a221SRafael J. Wysocki 452c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 4537420aa4fSUlf Hansson genpd_poweroff(genpd, false); 454c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 455f721889fSRafael J. Wysocki 456f721889fSRafael J. Wysocki return 0; 457f721889fSRafael J. Wysocki } 458f721889fSRafael J. Wysocki 459f721889fSRafael J. Wysocki /** 460f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 461f721889fSRafael J. Wysocki * @dev: Device to resume. 462f721889fSRafael J. Wysocki * 463f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 464f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 465f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 466f721889fSRafael J. Wysocki */ 467f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 468f721889fSRafael J. Wysocki { 469f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 4702b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 471ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 4722b1d88cdSUlf Hansson ktime_t time_start; 4732b1d88cdSUlf Hansson s64 elapsed_ns; 474f721889fSRafael J. Wysocki int ret; 475ba2bbfbfSUlf Hansson bool timed = true; 476f721889fSRafael J. Wysocki 477f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 478f721889fSRafael J. Wysocki 4795248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 4805248051bSRafael J. Wysocki if (IS_ERR(genpd)) 481f721889fSRafael J. Wysocki return -EINVAL; 482f721889fSRafael J. Wysocki 4830aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 484ba2bbfbfSUlf Hansson if (dev->power.irq_safe) { 485ba2bbfbfSUlf Hansson timed = false; 486ba2bbfbfSUlf Hansson goto out; 487ba2bbfbfSUlf Hansson } 4880aa2a221SRafael J. Wysocki 489c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 4900106ef51SMarek Szyprowski ret = __genpd_poweron(genpd, 0); 4913f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 492ba2bbfbfSUlf Hansson 493ba2bbfbfSUlf Hansson if (ret) 4943f241775SRafael J. Wysocki return ret; 495c6d22b37SRafael J. Wysocki 496ba2bbfbfSUlf Hansson out: 4972b1d88cdSUlf Hansson /* Measure resume latency. */ 498ffe12855SUlf Hansson if (timed && runtime_pm) 4992b1d88cdSUlf Hansson time_start = ktime_get(); 5002b1d88cdSUlf Hansson 5012b1d88cdSUlf Hansson genpd_start_dev(genpd, dev); 5022b1d88cdSUlf Hansson genpd_restore_dev(genpd, dev); 5032b1d88cdSUlf Hansson 5042b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 505ffe12855SUlf Hansson if (timed && runtime_pm) { 5062b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 5072b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 5082b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 5092b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 5102b1d88cdSUlf Hansson elapsed_ns); 5112b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 5122b1d88cdSUlf Hansson td->constraint_changed = true; 5132b1d88cdSUlf Hansson } 5142b1d88cdSUlf Hansson } 51517b75ecaSRafael J. Wysocki 516f721889fSRafael J. Wysocki return 0; 517f721889fSRafael J. Wysocki } 518f721889fSRafael J. Wysocki 51939ac5ba5STushar Behera static bool pd_ignore_unused; 52039ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 52139ac5ba5STushar Behera { 52239ac5ba5STushar Behera pd_ignore_unused = true; 52339ac5ba5STushar Behera return 1; 52439ac5ba5STushar Behera } 52539ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 52639ac5ba5STushar Behera 52717f2ae7fSRafael J. Wysocki /** 528bb4b72fcSUlf Hansson * genpd_poweroff_unused - Power off all PM domains with no devices in use. 52917f2ae7fSRafael J. Wysocki */ 530bb4b72fcSUlf Hansson static int __init genpd_poweroff_unused(void) 53117f2ae7fSRafael J. Wysocki { 53217f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 53317f2ae7fSRafael J. Wysocki 53439ac5ba5STushar Behera if (pd_ignore_unused) { 53539ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 536bb4b72fcSUlf Hansson return 0; 53739ac5ba5STushar Behera } 53839ac5ba5STushar Behera 53917f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 54017f2ae7fSRafael J. Wysocki 54117f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 54217f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 54317f2ae7fSRafael J. Wysocki 54417f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 54517f2ae7fSRafael J. Wysocki 5462fe71dcdSUlf Hansson return 0; 5472fe71dcdSUlf Hansson } 5482fe71dcdSUlf Hansson late_initcall(genpd_poweroff_unused); 5492fe71dcdSUlf Hansson 550596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 551596ba34bSRafael J. Wysocki 55277f827deSRafael J. Wysocki /** 55377f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 55477f827deSRafael J. Wysocki * @genpd: PM domain to check. 55577f827deSRafael J. Wysocki */ 556895b31f3SGeert Uytterhoeven static bool pm_genpd_present(const struct generic_pm_domain *genpd) 55777f827deSRafael J. Wysocki { 558895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 55977f827deSRafael J. Wysocki 56077f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 56177f827deSRafael J. Wysocki return false; 56277f827deSRafael J. Wysocki 56377f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 56477f827deSRafael J. Wysocki if (gpd == genpd) 56577f827deSRafael J. Wysocki return true; 56677f827deSRafael J. Wysocki 56777f827deSRafael J. Wysocki return false; 56877f827deSRafael J. Wysocki } 56977f827deSRafael J. Wysocki 570d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 571d5e4cbfeSRafael J. Wysocki struct device *dev) 572d5e4cbfeSRafael J. Wysocki { 573d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 574d5e4cbfeSRafael J. Wysocki } 575d5e4cbfeSRafael J. Wysocki 576596ba34bSRafael J. Wysocki /** 5775063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 578596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 579a4630c61SGeert Uytterhoeven * @timed: True if latency measurements are allowed. 580596ba34bSRafael J. Wysocki * 581596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 5825063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 583596ba34bSRafael J. Wysocki * 58477f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 58577f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 58677f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 58777f827deSRafael J. Wysocki * parallel). 588596ba34bSRafael J. Wysocki */ 589a4630c61SGeert Uytterhoeven static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, 590a4630c61SGeert Uytterhoeven bool timed) 591596ba34bSRafael J. Wysocki { 5925063ce15SRafael J. Wysocki struct gpd_link *link; 593596ba34bSRafael J. Wysocki 59417b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 595596ba34bSRafael J. Wysocki return; 596596ba34bSRafael J. Wysocki 597c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 598c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 599596ba34bSRafael J. Wysocki return; 600596ba34bSRafael J. Wysocki 601a4630c61SGeert Uytterhoeven genpd_power_off(genpd, timed); 602596ba34bSRafael J. Wysocki 60317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 6045063ce15SRafael J. Wysocki 6055063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 6065063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 607a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(link->master, timed); 608596ba34bSRafael J. Wysocki } 609596ba34bSRafael J. Wysocki } 610596ba34bSRafael J. Wysocki 611596ba34bSRafael J. Wysocki /** 612802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 613802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 614a4630c61SGeert Uytterhoeven * @timed: True if latency measurements are allowed. 615802d8b49SRafael J. Wysocki * 61677f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 61777f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 61877f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 61977f827deSRafael J. Wysocki * parallel). 620802d8b49SRafael J. Wysocki */ 621a4630c61SGeert Uytterhoeven static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd, 622a4630c61SGeert Uytterhoeven bool timed) 623802d8b49SRafael J. Wysocki { 624802d8b49SRafael J. Wysocki struct gpd_link *link; 625802d8b49SRafael J. Wysocki 626ba2bbfbfSUlf Hansson if (genpd->status == GPD_STATE_ACTIVE) 627802d8b49SRafael J. Wysocki return; 628802d8b49SRafael J. Wysocki 629802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 630a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(link->master, timed); 631802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 632802d8b49SRafael J. Wysocki } 633802d8b49SRafael J. Wysocki 634a4630c61SGeert Uytterhoeven genpd_power_on(genpd, timed); 635802d8b49SRafael J. Wysocki 636802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 637802d8b49SRafael J. Wysocki } 638802d8b49SRafael J. Wysocki 639802d8b49SRafael J. Wysocki /** 6404ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 6414ecd6e65SRafael J. Wysocki * @dev: Device to check. 6424ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 6434ecd6e65SRafael J. Wysocki * 6444ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 6454ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 6464ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 6474ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 6484ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 6494ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 6504ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 6514ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 6524ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 6534ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 6544ecd6e65SRafael J. Wysocki */ 6554ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 6564ecd6e65SRafael J. Wysocki { 6574ecd6e65SRafael J. Wysocki bool active_wakeup; 6584ecd6e65SRafael J. Wysocki 6594ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 6604ecd6e65SRafael J. Wysocki return false; 6614ecd6e65SRafael J. Wysocki 662d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 6634ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 6644ecd6e65SRafael J. Wysocki } 6654ecd6e65SRafael J. Wysocki 6664ecd6e65SRafael J. Wysocki /** 667596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 668596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 669596ba34bSRafael J. Wysocki * 670596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 671596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 672596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 673596ba34bSRafael J. Wysocki * consisting of I/O devices. 674596ba34bSRafael J. Wysocki */ 675596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 676596ba34bSRafael J. Wysocki { 677596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 678b6c10c84SRafael J. Wysocki int ret; 679596ba34bSRafael J. Wysocki 680596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 681596ba34bSRafael J. Wysocki 682596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 683596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 684596ba34bSRafael J. Wysocki return -EINVAL; 685596ba34bSRafael J. Wysocki 68617b75ecaSRafael J. Wysocki /* 68717b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 68817b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 68917b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 69017b75ecaSRafael J. Wysocki */ 69117b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 69217b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 69317b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 69417b75ecaSRafael J. Wysocki 69517b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 69684167035SUlf Hansson pm_runtime_put(dev); 69717b75ecaSRafael J. Wysocki return -EBUSY; 69817b75ecaSRafael J. Wysocki } 69917b75ecaSRafael J. Wysocki 7004ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 7014ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 7024ecd6e65SRafael J. Wysocki 703ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 704596ba34bSRafael J. Wysocki 70565533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 70665533bbfSRafael J. Wysocki genpd->suspended_count = 0; 70717b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 70865533bbfSRafael J. Wysocki } 70917b75ecaSRafael J. Wysocki 710ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 711596ba34bSRafael J. Wysocki 712596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 71317b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 714596ba34bSRafael J. Wysocki return 0; 715596ba34bSRafael J. Wysocki } 716596ba34bSRafael J. Wysocki 717596ba34bSRafael J. Wysocki /* 71817b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 7197420aa4fSUlf Hansson * so genpd_poweron() will return immediately, but if the device 720d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 72117b75ecaSRafael J. Wysocki * to make it operational. 722596ba34bSRafael J. Wysocki */ 72317b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 724596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 725596ba34bSRafael J. Wysocki 726b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 727b6c10c84SRafael J. Wysocki if (ret) { 728b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 729b6c10c84SRafael J. Wysocki 730b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 731b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 732b6c10c84SRafael J. Wysocki 733b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 73417b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 735b6c10c84SRafael J. Wysocki } 73617b75ecaSRafael J. Wysocki 73784167035SUlf Hansson pm_runtime_put(dev); 738b6c10c84SRafael J. Wysocki return ret; 739596ba34bSRafael J. Wysocki } 740596ba34bSRafael J. Wysocki 741596ba34bSRafael J. Wysocki /** 742596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 743596ba34bSRafael J. Wysocki * @dev: Device to suspend. 744596ba34bSRafael J. Wysocki * 745596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 746596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 747596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 748596ba34bSRafael J. Wysocki */ 749596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 750596ba34bSRafael J. Wysocki { 751596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 752596ba34bSRafael J. Wysocki 753596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 754596ba34bSRafael J. Wysocki 755596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 756596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 757596ba34bSRafael J. Wysocki return -EINVAL; 758596ba34bSRafael J. Wysocki 7591e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev); 760596ba34bSRafael J. Wysocki } 761596ba34bSRafael J. Wysocki 762596ba34bSRafael J. Wysocki /** 7630496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 764596ba34bSRafael J. Wysocki * @dev: Device to suspend. 765596ba34bSRafael J. Wysocki * 766596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 767596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 768596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 769596ba34bSRafael J. Wysocki */ 7700496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 771596ba34bSRafael J. Wysocki { 772596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 773596ba34bSRafael J. Wysocki 774596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 775596ba34bSRafael J. Wysocki 776596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 777596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 778596ba34bSRafael J. Wysocki return -EINVAL; 779596ba34bSRafael J. Wysocki 7801e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_suspend_late(dev); 7810496c8aeSRafael J. Wysocki } 782596ba34bSRafael J. Wysocki 7830496c8aeSRafael J. Wysocki /** 7840496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 7850496c8aeSRafael J. Wysocki * @dev: Device to suspend. 7860496c8aeSRafael J. Wysocki * 7870496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 7880496c8aeSRafael J. Wysocki * been stopped. 7890496c8aeSRafael J. Wysocki */ 7900496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 7910496c8aeSRafael J. Wysocki { 7920496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 793596ba34bSRafael J. Wysocki 7940496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 7950496c8aeSRafael J. Wysocki 7960496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 7970496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 7980496c8aeSRafael J. Wysocki return -EINVAL; 7990496c8aeSRafael J. Wysocki 800dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 8010496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 802d4f2d87aSRafael J. Wysocki return 0; 803d4f2d87aSRafael J. Wysocki 8042b1d88cdSUlf Hansson genpd_stop_dev(genpd, dev); 805596ba34bSRafael J. Wysocki 806596ba34bSRafael J. Wysocki /* 807596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 808596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 809596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 810596ba34bSRafael J. Wysocki */ 811596ba34bSRafael J. Wysocki genpd->suspended_count++; 812a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(genpd, true); 813596ba34bSRafael J. Wysocki 814596ba34bSRafael J. Wysocki return 0; 815596ba34bSRafael J. Wysocki } 816596ba34bSRafael J. Wysocki 817596ba34bSRafael J. Wysocki /** 8180496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 819596ba34bSRafael J. Wysocki * @dev: Device to resume. 820596ba34bSRafael J. Wysocki * 8210496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 822596ba34bSRafael J. Wysocki */ 823596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 824596ba34bSRafael J. Wysocki { 825596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 826596ba34bSRafael J. Wysocki 827596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 828596ba34bSRafael J. Wysocki 829596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 830596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 831596ba34bSRafael J. Wysocki return -EINVAL; 832596ba34bSRafael J. Wysocki 833dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 834cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 835596ba34bSRafael J. Wysocki return 0; 836596ba34bSRafael J. Wysocki 837596ba34bSRafael J. Wysocki /* 838596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 839596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 840596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 841596ba34bSRafael J. Wysocki */ 842a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, true); 843596ba34bSRafael J. Wysocki genpd->suspended_count--; 844596ba34bSRafael J. Wysocki 8452b1d88cdSUlf Hansson return genpd_start_dev(genpd, dev); 846596ba34bSRafael J. Wysocki } 847596ba34bSRafael J. Wysocki 848596ba34bSRafael J. Wysocki /** 8490496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 8500496c8aeSRafael J. Wysocki * @dev: Device to resume. 8510496c8aeSRafael J. Wysocki * 8520496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 8530496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 8540496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 8550496c8aeSRafael J. Wysocki * devices. 8560496c8aeSRafael J. Wysocki */ 8570496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 8580496c8aeSRafael J. Wysocki { 8590496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 8600496c8aeSRafael J. Wysocki 8610496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8620496c8aeSRafael J. Wysocki 8630496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 8640496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 8650496c8aeSRafael J. Wysocki return -EINVAL; 8660496c8aeSRafael J. Wysocki 8671e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_resume_early(dev); 8680496c8aeSRafael J. Wysocki } 8690496c8aeSRafael J. Wysocki 8700496c8aeSRafael J. Wysocki /** 8710496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 872596ba34bSRafael J. Wysocki * @dev: Device to resume. 873596ba34bSRafael J. Wysocki * 874596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 875596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 876596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 877596ba34bSRafael J. Wysocki */ 878596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 879596ba34bSRafael J. Wysocki { 880596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 881596ba34bSRafael J. Wysocki 882596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 883596ba34bSRafael J. Wysocki 884596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 885596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 886596ba34bSRafael J. Wysocki return -EINVAL; 887596ba34bSRafael J. Wysocki 8881e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_resume(dev); 889596ba34bSRafael J. Wysocki } 890596ba34bSRafael J. Wysocki 891596ba34bSRafael J. Wysocki /** 8920496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 893596ba34bSRafael J. Wysocki * @dev: Device to freeze. 894596ba34bSRafael J. Wysocki * 895596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 896596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 897596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 898596ba34bSRafael J. Wysocki */ 899596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 900596ba34bSRafael J. Wysocki { 901596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 902596ba34bSRafael J. Wysocki 903596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 904596ba34bSRafael J. Wysocki 905596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 906596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 907596ba34bSRafael J. Wysocki return -EINVAL; 908596ba34bSRafael J. Wysocki 9091e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev); 910596ba34bSRafael J. Wysocki } 911596ba34bSRafael J. Wysocki 912596ba34bSRafael J. Wysocki /** 9130496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 9140496c8aeSRafael J. Wysocki * @dev: Device to freeze. 9150496c8aeSRafael J. Wysocki * 9160496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 9170496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9180496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9190496c8aeSRafael J. Wysocki * devices. 9200496c8aeSRafael J. Wysocki */ 9210496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 9220496c8aeSRafael J. Wysocki { 9230496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 9240496c8aeSRafael J. Wysocki 9250496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9260496c8aeSRafael J. Wysocki 9270496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9280496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9290496c8aeSRafael J. Wysocki return -EINVAL; 9300496c8aeSRafael J. Wysocki 9311e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_freeze_late(dev); 9320496c8aeSRafael J. Wysocki } 9330496c8aeSRafael J. Wysocki 9340496c8aeSRafael J. Wysocki /** 9350496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 936596ba34bSRafael J. Wysocki * @dev: Device to freeze. 937596ba34bSRafael J. Wysocki * 938596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 939596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 940596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 941596ba34bSRafael J. Wysocki * devices. 942596ba34bSRafael J. Wysocki */ 943596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 944596ba34bSRafael J. Wysocki { 945596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 946596ba34bSRafael J. Wysocki 947596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 948596ba34bSRafael J. Wysocki 949596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 950596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 951596ba34bSRafael J. Wysocki return -EINVAL; 952596ba34bSRafael J. Wysocki 9532b1d88cdSUlf Hansson return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); 954596ba34bSRafael J. Wysocki } 955596ba34bSRafael J. Wysocki 956596ba34bSRafael J. Wysocki /** 9570496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 958596ba34bSRafael J. Wysocki * @dev: Device to thaw. 959596ba34bSRafael J. Wysocki * 9600496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 9610496c8aeSRafael J. Wysocki * before the system transition. 962596ba34bSRafael J. Wysocki */ 963596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 964596ba34bSRafael J. Wysocki { 965596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 966596ba34bSRafael J. Wysocki 967596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 968596ba34bSRafael J. Wysocki 969596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 970596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 971596ba34bSRafael J. Wysocki return -EINVAL; 972596ba34bSRafael J. Wysocki 97351cda844SUlf Hansson return genpd->suspend_power_off ? 9742b1d88cdSUlf Hansson 0 : genpd_start_dev(genpd, dev); 9750496c8aeSRafael J. Wysocki } 976596ba34bSRafael J. Wysocki 9770496c8aeSRafael J. Wysocki /** 9780496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 9790496c8aeSRafael J. Wysocki * @dev: Device to thaw. 9800496c8aeSRafael J. Wysocki * 9810496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 9820496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9830496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9840496c8aeSRafael J. Wysocki * devices. 9850496c8aeSRafael J. Wysocki */ 9860496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 9870496c8aeSRafael J. Wysocki { 9880496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 989596ba34bSRafael J. Wysocki 9900496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9910496c8aeSRafael J. Wysocki 9920496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9930496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9940496c8aeSRafael J. Wysocki return -EINVAL; 9950496c8aeSRafael J. Wysocki 9961e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_thaw_early(dev); 997596ba34bSRafael J. Wysocki } 998596ba34bSRafael J. Wysocki 999596ba34bSRafael J. Wysocki /** 1000596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1001596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1002596ba34bSRafael J. Wysocki * 1003596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1004596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1005596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1006596ba34bSRafael J. Wysocki */ 1007596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1008596ba34bSRafael J. Wysocki { 1009596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1010596ba34bSRafael J. Wysocki 1011596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1012596ba34bSRafael J. Wysocki 1013596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1014596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1015596ba34bSRafael J. Wysocki return -EINVAL; 1016596ba34bSRafael J. Wysocki 10171e0407caSUlf Hansson return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev); 1018596ba34bSRafael J. Wysocki } 1019596ba34bSRafael J. Wysocki 1020596ba34bSRafael J. Wysocki /** 10210496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1022596ba34bSRafael J. Wysocki * @dev: Device to resume. 1023596ba34bSRafael J. Wysocki * 10240496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 10250496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1026596ba34bSRafael J. Wysocki */ 1027596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1028596ba34bSRafael J. Wysocki { 1029596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1030596ba34bSRafael J. Wysocki 1031596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1032596ba34bSRafael J. Wysocki 1033596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1034596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1035596ba34bSRafael J. Wysocki return -EINVAL; 1036596ba34bSRafael J. Wysocki 1037596ba34bSRafael J. Wysocki /* 1038596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1039596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1040596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 104165533bbfSRafael J. Wysocki * 104265533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 104365533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 104465533bbfSRafael J. Wysocki */ 104565533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 104665533bbfSRafael J. Wysocki /* 104765533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1048802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 1049802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1050596ba34bSRafael J. Wysocki */ 105117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1052596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1053596ba34bSRafael J. Wysocki /* 105465533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 105565533bbfSRafael J. Wysocki * sure it will be off going forward. 1056596ba34bSRafael J. Wysocki */ 1057a4630c61SGeert Uytterhoeven genpd_power_off(genpd, true); 105865533bbfSRafael J. Wysocki 1059596ba34bSRafael J. Wysocki return 0; 1060596ba34bSRafael J. Wysocki } 106165533bbfSRafael J. Wysocki } 1062596ba34bSRafael J. Wysocki 106318dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 106418dd2eceSRafael J. Wysocki return 0; 106518dd2eceSRafael J. Wysocki 1066a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, true); 1067596ba34bSRafael J. Wysocki 10682b1d88cdSUlf Hansson return genpd_start_dev(genpd, dev); 1069596ba34bSRafael J. Wysocki } 1070596ba34bSRafael J. Wysocki 1071596ba34bSRafael J. Wysocki /** 1072596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1073596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1074596ba34bSRafael J. Wysocki * 1075596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1076596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1077596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1078596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1079596ba34bSRafael J. Wysocki */ 1080596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1081596ba34bSRafael J. Wysocki { 1082596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1083596ba34bSRafael J. Wysocki bool run_complete; 1084596ba34bSRafael J. Wysocki 1085596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1086596ba34bSRafael J. Wysocki 1087596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1088596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1089596ba34bSRafael J. Wysocki return; 1090596ba34bSRafael J. Wysocki 1091596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1092596ba34bSRafael J. Wysocki 1093596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1094596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1095596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1096596ba34bSRafael J. Wysocki 1097596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1098596ba34bSRafael J. Wysocki 1099596ba34bSRafael J. Wysocki if (run_complete) { 1100596ba34bSRafael J. Wysocki pm_generic_complete(dev); 11016f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1102596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 1103af939339SUlf Hansson pm_request_idle(dev); 1104596ba34bSRafael J. Wysocki } 1105596ba34bSRafael J. Wysocki } 1106596ba34bSRafael J. Wysocki 110777f827deSRafael J. Wysocki /** 1108d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 110977f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 111077f827deSRafael J. Wysocki * 111177f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 111277f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 111377f827deSRafael J. Wysocki */ 1114d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 111577f827deSRafael J. Wysocki { 111677f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 111777f827deSRafael J. Wysocki 111877f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 111977f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 112077f827deSRafael J. Wysocki return; 112177f827deSRafael J. Wysocki 112277f827deSRafael J. Wysocki if (suspend) { 112377f827deSRafael J. Wysocki genpd->suspended_count++; 1124a4630c61SGeert Uytterhoeven pm_genpd_sync_poweroff(genpd, false); 112577f827deSRafael J. Wysocki } else { 1126a4630c61SGeert Uytterhoeven pm_genpd_sync_poweron(genpd, false); 112777f827deSRafael J. Wysocki genpd->suspended_count--; 112877f827deSRafael J. Wysocki } 112977f827deSRafael J. Wysocki } 1130d47e6464SUlf Hansson 1131d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1132d47e6464SUlf Hansson { 1133d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1134d47e6464SUlf Hansson } 1135d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1136d47e6464SUlf Hansson 1137d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1138d47e6464SUlf Hansson { 1139d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1140d47e6464SUlf Hansson } 1141d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 114277f827deSRafael J. Wysocki 1143d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1144596ba34bSRafael J. Wysocki 1145596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1146596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 11470496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1148596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 11490496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1150596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1151596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1152596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 11530496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1154596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 11550496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1156596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1157596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1158596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1159596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1160596ba34bSRafael J. Wysocki 1161596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1162596ba34bSRafael J. Wysocki 1163f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1164f104e1e5SUlf Hansson struct generic_pm_domain *genpd, 1165f104e1e5SUlf Hansson struct gpd_timing_data *td) 11661d5fcfecSRafael J. Wysocki { 11671d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 11683e235685SUlf Hansson int ret; 11693e235685SUlf Hansson 11703e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 11713e235685SUlf Hansson if (ret) 11723e235685SUlf Hansson return ERR_PTR(ret); 11731d5fcfecSRafael J. Wysocki 11741d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 11753e235685SUlf Hansson if (!gpd_data) { 11763e235685SUlf Hansson ret = -ENOMEM; 11773e235685SUlf Hansson goto err_put; 11783e235685SUlf Hansson } 11791d5fcfecSRafael J. Wysocki 1180f104e1e5SUlf Hansson if (td) 1181f104e1e5SUlf Hansson gpd_data->td = *td; 1182f104e1e5SUlf Hansson 1183f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1184f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 1185f104e1e5SUlf Hansson gpd_data->td.effective_constraint_ns = -1; 1186f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1187f104e1e5SUlf Hansson 1188f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1189f104e1e5SUlf Hansson 1190f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1191f104e1e5SUlf Hansson ret = -EINVAL; 1192f104e1e5SUlf Hansson goto err_free; 1193f104e1e5SUlf Hansson } 1194f104e1e5SUlf Hansson 1195f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1196f104e1e5SUlf Hansson dev->pm_domain = &genpd->domain; 1197f104e1e5SUlf Hansson 1198f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1199f104e1e5SUlf Hansson 12001d5fcfecSRafael J. Wysocki return gpd_data; 12013e235685SUlf Hansson 1202f104e1e5SUlf Hansson err_free: 1203f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1204f104e1e5SUlf Hansson kfree(gpd_data); 12053e235685SUlf Hansson err_put: 12063e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12073e235685SUlf Hansson return ERR_PTR(ret); 12081d5fcfecSRafael J. Wysocki } 12091d5fcfecSRafael J. Wysocki 121049d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 12111d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 12121d5fcfecSRafael J. Wysocki { 1213f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1214f104e1e5SUlf Hansson 1215f104e1e5SUlf Hansson dev->pm_domain = NULL; 1216f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1217f104e1e5SUlf Hansson 1218f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1219f104e1e5SUlf Hansson 12201d5fcfecSRafael J. Wysocki kfree(gpd_data); 12213e235685SUlf Hansson dev_pm_put_subsys_data(dev); 12221d5fcfecSRafael J. Wysocki } 12231d5fcfecSRafael J. Wysocki 1224f721889fSRafael J. Wysocki /** 1225b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1226f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1227f721889fSRafael J. Wysocki * @dev: Device to be added. 1228b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1229f721889fSRafael J. Wysocki */ 1230b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1231b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1232f721889fSRafael J. Wysocki { 1233c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1234f721889fSRafael J. Wysocki int ret = 0; 1235f721889fSRafael J. Wysocki 1236f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1237f721889fSRafael J. Wysocki 1238f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1239f721889fSRafael J. Wysocki return -EINVAL; 1240f721889fSRafael J. Wysocki 1241f104e1e5SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, genpd, td); 12423e235685SUlf Hansson if (IS_ERR(gpd_data)) 12433e235685SUlf Hansson return PTR_ERR(gpd_data); 12446ff7bb0dSRafael J. Wysocki 1245ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1246f721889fSRafael J. Wysocki 1247596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1248596ba34bSRafael J. Wysocki ret = -EAGAIN; 1249596ba34bSRafael J. Wysocki goto out; 1250596ba34bSRafael J. Wysocki } 1251596ba34bSRafael J. Wysocki 1252b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1253b472c2faSUlf Hansson if (ret) 1254b472c2faSUlf Hansson goto out; 1255d79b6fe1SGeert Uytterhoeven 125614b53064SUlf Hansson genpd->device_count++; 125714b53064SUlf Hansson genpd->max_off_time_changed = true; 125814b53064SUlf Hansson 12591d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 12606ff7bb0dSRafael J. Wysocki 1261f721889fSRafael J. Wysocki out: 1262ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1263f721889fSRafael J. Wysocki 1264c0356db7SUlf Hansson if (ret) 1265c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1266c0356db7SUlf Hansson else 1267c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12681d5fcfecSRafael J. Wysocki 1269f721889fSRafael J. Wysocki return ret; 1270f721889fSRafael J. Wysocki } 127124c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(__pm_genpd_add_device); 1272f721889fSRafael J. Wysocki 1273f721889fSRafael J. Wysocki /** 1274f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1275f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1276f721889fSRafael J. Wysocki * @dev: Device to be removed. 1277f721889fSRafael J. Wysocki */ 1278f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1279f721889fSRafael J. Wysocki struct device *dev) 1280f721889fSRafael J. Wysocki { 12816ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12824605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1283efa69025SRafael J. Wysocki int ret = 0; 1284f721889fSRafael J. Wysocki 1285f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1286f721889fSRafael J. Wysocki 1287df6a0d6fSRussell King if (!genpd || genpd != pm_genpd_lookup_dev(dev)) 1288f721889fSRafael J. Wysocki return -EINVAL; 1289f721889fSRafael J. Wysocki 1290c0356db7SUlf Hansson /* The above validation also means we have existing domain_data. */ 1291c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1292c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1293c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1294c0356db7SUlf Hansson 1295ba2bbfbfSUlf Hansson mutex_lock(&genpd->lock); 1296f721889fSRafael J. Wysocki 1297596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1298596ba34bSRafael J. Wysocki ret = -EAGAIN; 1299596ba34bSRafael J. Wysocki goto out; 1300596ba34bSRafael J. Wysocki } 1301596ba34bSRafael J. Wysocki 13026ff7bb0dSRafael J. Wysocki genpd->device_count--; 13036ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 13046ff7bb0dSRafael J. Wysocki 1305d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1306c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1307d79b6fe1SGeert Uytterhoeven 1308efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 13096ff7bb0dSRafael J. Wysocki 1310ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 13116ff7bb0dSRafael J. Wysocki 131249d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 13131d5fcfecSRafael J. Wysocki 13146ff7bb0dSRafael J. Wysocki return 0; 1315f721889fSRafael J. Wysocki 1316596ba34bSRafael J. Wysocki out: 1317ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1318c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1319f721889fSRafael J. Wysocki 1320f721889fSRafael J. Wysocki return ret; 1321f721889fSRafael J. Wysocki } 132224c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1323f721889fSRafael J. Wysocki 1324f721889fSRafael J. Wysocki /** 1325f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1326f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1327bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1328f721889fSRafael J. Wysocki */ 1329f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1330bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1331f721889fSRafael J. Wysocki { 13322547923dSLina Iyer struct gpd_link *link, *itr; 1333f721889fSRafael J. Wysocki int ret = 0; 1334f721889fSRafael J. Wysocki 1335fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1336fb7268beSRafael J. Wysocki || genpd == subdomain) 1337f721889fSRafael J. Wysocki return -EINVAL; 1338f721889fSRafael J. Wysocki 13392547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 13402547923dSLina Iyer if (!link) 13412547923dSLina Iyer return -ENOMEM; 13422547923dSLina Iyer 1343cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1344cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1345f721889fSRafael J. Wysocki 134617b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1347bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1348f721889fSRafael J. Wysocki ret = -EINVAL; 1349f721889fSRafael J. Wysocki goto out; 1350f721889fSRafael J. Wysocki } 1351f721889fSRafael J. Wysocki 13522547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 13532547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1354f721889fSRafael J. Wysocki ret = -EINVAL; 1355f721889fSRafael J. Wysocki goto out; 1356f721889fSRafael J. Wysocki } 1357f721889fSRafael J. Wysocki } 1358f721889fSRafael J. Wysocki 13595063ce15SRafael J. Wysocki link->master = genpd; 13605063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1361bc0403ffSRafael J. Wysocki link->slave = subdomain; 1362bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1363bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1364c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1365f721889fSRafael J. Wysocki 1366f721889fSRafael J. Wysocki out: 1367ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1368cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 13692547923dSLina Iyer if (ret) 13702547923dSLina Iyer kfree(link); 1371f721889fSRafael J. Wysocki return ret; 1372f721889fSRafael J. Wysocki } 1373d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1374f721889fSRafael J. Wysocki 1375f721889fSRafael J. Wysocki /** 1376f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1377f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 13785063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1379f721889fSRafael J. Wysocki */ 1380f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 13815063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1382f721889fSRafael J. Wysocki { 13835063ce15SRafael J. Wysocki struct gpd_link *link; 1384f721889fSRafael J. Wysocki int ret = -EINVAL; 1385f721889fSRafael J. Wysocki 13865063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1387f721889fSRafael J. Wysocki return -EINVAL; 1388f721889fSRafael J. Wysocki 1389cdb300a0SUlf Hansson mutex_lock(&subdomain->lock); 1390cdb300a0SUlf Hansson mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); 1391f721889fSRafael J. Wysocki 139230e7a65bSJon Hunter if (!list_empty(&subdomain->slave_links) || subdomain->device_count) { 139330e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 139430e7a65bSJon Hunter subdomain->name); 139530e7a65bSJon Hunter ret = -EBUSY; 139630e7a65bSJon Hunter goto out; 139730e7a65bSJon Hunter } 139830e7a65bSJon Hunter 13995063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 14005063ce15SRafael J. Wysocki if (link->slave != subdomain) 1401f721889fSRafael J. Wysocki continue; 1402f721889fSRafael J. Wysocki 14035063ce15SRafael J. Wysocki list_del(&link->master_node); 14045063ce15SRafael J. Wysocki list_del(&link->slave_node); 14055063ce15SRafael J. Wysocki kfree(link); 140617b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1407f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1408f721889fSRafael J. Wysocki 1409f721889fSRafael J. Wysocki ret = 0; 1410f721889fSRafael J. Wysocki break; 1411f721889fSRafael J. Wysocki } 1412f721889fSRafael J. Wysocki 141330e7a65bSJon Hunter out: 1414ba2bbfbfSUlf Hansson mutex_unlock(&genpd->lock); 1415cdb300a0SUlf Hansson mutex_unlock(&subdomain->lock); 1416f721889fSRafael J. Wysocki 1417f721889fSRafael J. Wysocki return ret; 1418f721889fSRafael J. Wysocki } 1419d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1420f721889fSRafael J. Wysocki 1421d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1422d23b9b00SRafael J. Wysocki 1423d5e4cbfeSRafael J. Wysocki /** 142412e10bb6SGeert Uytterhoeven * pm_genpd_default_save_state - Default "save device state" for PM domains. 1425ecf00475SRafael J. Wysocki * @dev: Device to handle. 1426ecf00475SRafael J. Wysocki */ 1427ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1428ecf00475SRafael J. Wysocki { 1429ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1430ecf00475SRafael J. Wysocki 14310b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 14320b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 14330b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 14340b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 14350b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 14360b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 14370b589741SRafael J. Wysocki else 14380b589741SRafael J. Wysocki cb = NULL; 1439ecf00475SRafael J. Wysocki 14400b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 14410b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 14420b589741SRafael J. Wysocki 14430b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1444ecf00475SRafael J. Wysocki } 1445ecf00475SRafael J. Wysocki 1446ecf00475SRafael J. Wysocki /** 144712e10bb6SGeert Uytterhoeven * pm_genpd_default_restore_state - Default PM domains "restore device state". 1448ecf00475SRafael J. Wysocki * @dev: Device to handle. 1449ecf00475SRafael J. Wysocki */ 1450ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1451ecf00475SRafael J. Wysocki { 1452ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1453ecf00475SRafael J. Wysocki 14540b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 14550b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 14560b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 14570b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 14580b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 14590b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 14600b589741SRafael J. Wysocki else 14610b589741SRafael J. Wysocki cb = NULL; 1462ecf00475SRafael J. Wysocki 14630b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 14640b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 14650b589741SRafael J. Wysocki 14660b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1467ecf00475SRafael J. Wysocki } 1468ecf00475SRafael J. Wysocki 1469d23b9b00SRafael J. Wysocki /** 1470f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1471f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1472f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1473f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1474f721889fSRafael J. Wysocki */ 1475f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1476f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1477f721889fSRafael J. Wysocki { 1478f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1479f721889fSRafael J. Wysocki return; 1480f721889fSRafael J. Wysocki 14815063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 14825063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1483f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1484f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1485f721889fSRafael J. Wysocki genpd->gov = gov; 1486f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1487c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 148817b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1489596ba34bSRafael J. Wysocki genpd->device_count = 0; 1490221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 14916ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1492f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1493f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1494596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1495596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 14960496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 1497596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1498596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 14990496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 1500596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1501596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 15020496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 1503596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1504596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 15050496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 1506596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1507d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 15080496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 1509d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1510596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 15110496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 1512d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 1513596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1514ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 1515ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 1516c11f6f5bSUlf Hansson 1517c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1518c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1519c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1520c11f6f5bSUlf Hansson } 1521c11f6f5bSUlf Hansson 15225125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 15235125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 15245125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 15255125bbf3SRafael J. Wysocki } 1526be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1527aa42240aSTomasz Figa 1528aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1529aa42240aSTomasz Figa /* 1530aa42240aSTomasz Figa * Device Tree based PM domain providers. 1531aa42240aSTomasz Figa * 1532aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1533aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1534aa42240aSTomasz Figa * 1535aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1536aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1537aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1538aa42240aSTomasz Figa * 1539aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1540aa42240aSTomasz Figa * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1541aa42240aSTomasz Figa * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by 1542aa42240aSTomasz Figa * index. 1543aa42240aSTomasz Figa */ 1544aa42240aSTomasz Figa 1545aa42240aSTomasz Figa /** 1546aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1547aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1548aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1549aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1550aa42240aSTomasz Figa * into a PM domain. 1551aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1552aa42240aSTomasz Figa */ 1553aa42240aSTomasz Figa struct of_genpd_provider { 1554aa42240aSTomasz Figa struct list_head link; 1555aa42240aSTomasz Figa struct device_node *node; 1556aa42240aSTomasz Figa genpd_xlate_t xlate; 1557aa42240aSTomasz Figa void *data; 1558aa42240aSTomasz Figa }; 1559aa42240aSTomasz Figa 1560aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1561aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1562aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1563aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1564aa42240aSTomasz Figa 1565aa42240aSTomasz Figa /** 1566aa42240aSTomasz Figa * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping 1567aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1568aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1569aa42240aSTomasz Figa * 1570aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1571aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1572aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1573aa42240aSTomasz Figa */ 1574aa42240aSTomasz Figa struct generic_pm_domain *__of_genpd_xlate_simple( 1575aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1576aa42240aSTomasz Figa void *data) 1577aa42240aSTomasz Figa { 1578aa42240aSTomasz Figa if (genpdspec->args_count != 0) 1579aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1580aa42240aSTomasz Figa return data; 1581aa42240aSTomasz Figa } 1582aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple); 1583aa42240aSTomasz Figa 1584aa42240aSTomasz Figa /** 1585aa42240aSTomasz Figa * __of_genpd_xlate_onecell() - Xlate function using a single index. 1586aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1587aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1588aa42240aSTomasz Figa * 1589aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1590aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1591aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1592aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1593aa42240aSTomasz Figa */ 1594aa42240aSTomasz Figa struct generic_pm_domain *__of_genpd_xlate_onecell( 1595aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1596aa42240aSTomasz Figa void *data) 1597aa42240aSTomasz Figa { 1598aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1599aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1600aa42240aSTomasz Figa 1601aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1602aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1603aa42240aSTomasz Figa 1604aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1605aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1606aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1607aa42240aSTomasz Figa } 1608aa42240aSTomasz Figa 1609aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1610aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1611aa42240aSTomasz Figa 1612aa42240aSTomasz Figa return genpd_data->domains[idx]; 1613aa42240aSTomasz Figa } 1614aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell); 1615aa42240aSTomasz Figa 1616aa42240aSTomasz Figa /** 1617aa42240aSTomasz Figa * __of_genpd_add_provider() - Register a PM domain provider for a node 1618aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1619aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1620aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1621aa42240aSTomasz Figa */ 1622aa42240aSTomasz Figa int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1623aa42240aSTomasz Figa void *data) 1624aa42240aSTomasz Figa { 1625aa42240aSTomasz Figa struct of_genpd_provider *cp; 1626aa42240aSTomasz Figa 1627aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1628aa42240aSTomasz Figa if (!cp) 1629aa42240aSTomasz Figa return -ENOMEM; 1630aa42240aSTomasz Figa 1631aa42240aSTomasz Figa cp->node = of_node_get(np); 1632aa42240aSTomasz Figa cp->data = data; 1633aa42240aSTomasz Figa cp->xlate = xlate; 1634aa42240aSTomasz Figa 1635aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1636aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1637aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1638aa42240aSTomasz Figa pr_debug("Added domain provider from %s\n", np->full_name); 1639aa42240aSTomasz Figa 1640aa42240aSTomasz Figa return 0; 1641aa42240aSTomasz Figa } 1642aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(__of_genpd_add_provider); 1643aa42240aSTomasz Figa 1644aa42240aSTomasz Figa /** 1645aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 1646aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 1647aa42240aSTomasz Figa */ 1648aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 1649aa42240aSTomasz Figa { 1650aa42240aSTomasz Figa struct of_genpd_provider *cp; 1651aa42240aSTomasz Figa 1652aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1653aa42240aSTomasz Figa list_for_each_entry(cp, &of_genpd_providers, link) { 1654aa42240aSTomasz Figa if (cp->node == np) { 1655aa42240aSTomasz Figa list_del(&cp->link); 1656aa42240aSTomasz Figa of_node_put(cp->node); 1657aa42240aSTomasz Figa kfree(cp); 1658aa42240aSTomasz Figa break; 1659aa42240aSTomasz Figa } 1660aa42240aSTomasz Figa } 1661aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1662aa42240aSTomasz Figa } 1663aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 1664aa42240aSTomasz Figa 1665aa42240aSTomasz Figa /** 1666aa42240aSTomasz Figa * of_genpd_get_from_provider() - Look-up PM domain 1667aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 1668aa42240aSTomasz Figa * 1669aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 1670aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 1671aa42240aSTomasz Figa * domain. 1672aa42240aSTomasz Figa * 1673aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 1674aa42240aSTomasz Figa * on failure. 1675aa42240aSTomasz Figa */ 16767496fcbeSAmit Daniel Kachhap struct generic_pm_domain *of_genpd_get_from_provider( 1677aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 1678aa42240aSTomasz Figa { 1679aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 1680aa42240aSTomasz Figa struct of_genpd_provider *provider; 1681aa42240aSTomasz Figa 1682aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1683aa42240aSTomasz Figa 1684aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 1685aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 1686aa42240aSTomasz Figa if (provider->node == genpdspec->np) 1687aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 1688aa42240aSTomasz Figa if (!IS_ERR(genpd)) 1689aa42240aSTomasz Figa break; 1690aa42240aSTomasz Figa } 1691aa42240aSTomasz Figa 1692aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1693aa42240aSTomasz Figa 1694aa42240aSTomasz Figa return genpd; 1695aa42240aSTomasz Figa } 16967496fcbeSAmit Daniel Kachhap EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); 1697aa42240aSTomasz Figa 1698aa42240aSTomasz Figa /** 1699aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 17008bb6944eSJon Hunter * @dev: Device to detach. 1701aa42240aSTomasz Figa * @power_off: Currently not used 1702aa42240aSTomasz Figa * 1703aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 1704aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 1705aa42240aSTomasz Figa */ 1706aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 1707aa42240aSTomasz Figa { 1708446d999cSRussell King struct generic_pm_domain *pd; 170993af5e93SGeert Uytterhoeven unsigned int i; 1710aa42240aSTomasz Figa int ret = 0; 1711aa42240aSTomasz Figa 1712446d999cSRussell King pd = pm_genpd_lookup_dev(dev); 1713aa42240aSTomasz Figa if (!pd) 1714aa42240aSTomasz Figa return; 1715aa42240aSTomasz Figa 1716aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 1717aa42240aSTomasz Figa 171893af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1719aa42240aSTomasz Figa ret = pm_genpd_remove_device(pd, dev); 1720aa42240aSTomasz Figa if (ret != -EAGAIN) 1721aa42240aSTomasz Figa break; 172293af5e93SGeert Uytterhoeven 172393af5e93SGeert Uytterhoeven mdelay(i); 1724aa42240aSTomasz Figa cond_resched(); 1725aa42240aSTomasz Figa } 1726aa42240aSTomasz Figa 1727aa42240aSTomasz Figa if (ret < 0) { 1728aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 1729aa42240aSTomasz Figa pd->name, ret); 1730aa42240aSTomasz Figa return; 1731aa42240aSTomasz Figa } 1732aa42240aSTomasz Figa 1733aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 1734aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 1735aa42240aSTomasz Figa } 1736aa42240aSTomasz Figa 1737632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 1738632f7ce3SRussell King { 1739632f7ce3SRussell King struct generic_pm_domain *pd; 1740632f7ce3SRussell King 1741632f7ce3SRussell King pd = dev_to_genpd(dev); 1742632f7ce3SRussell King if (IS_ERR(pd)) 1743632f7ce3SRussell King return; 1744632f7ce3SRussell King 1745632f7ce3SRussell King genpd_queue_power_off_work(pd); 1746632f7ce3SRussell King } 1747632f7ce3SRussell King 1748aa42240aSTomasz Figa /** 1749aa42240aSTomasz Figa * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 1750aa42240aSTomasz Figa * @dev: Device to attach. 1751aa42240aSTomasz Figa * 1752aa42240aSTomasz Figa * Parse device's OF node to find a PM domain specifier. If such is found, 1753aa42240aSTomasz Figa * attaches the device to retrieved pm_domain ops. 1754aa42240aSTomasz Figa * 1755aa42240aSTomasz Figa * Both generic and legacy Samsung-specific DT bindings are supported to keep 1756aa42240aSTomasz Figa * backwards compatibility with existing DTBs. 1757aa42240aSTomasz Figa * 1758311fa6adSJon Hunter * Returns 0 on successfully attached PM domain or negative error code. Note 1759311fa6adSJon Hunter * that if a power-domain exists for the device, but it cannot be found or 1760311fa6adSJon Hunter * turned on, then return -EPROBE_DEFER to ensure that the device is not 1761311fa6adSJon Hunter * probed and to re-try again later. 1762aa42240aSTomasz Figa */ 1763aa42240aSTomasz Figa int genpd_dev_pm_attach(struct device *dev) 1764aa42240aSTomasz Figa { 1765aa42240aSTomasz Figa struct of_phandle_args pd_args; 1766aa42240aSTomasz Figa struct generic_pm_domain *pd; 176793af5e93SGeert Uytterhoeven unsigned int i; 1768aa42240aSTomasz Figa int ret; 1769aa42240aSTomasz Figa 1770aa42240aSTomasz Figa if (!dev->of_node) 1771aa42240aSTomasz Figa return -ENODEV; 1772aa42240aSTomasz Figa 1773aa42240aSTomasz Figa if (dev->pm_domain) 1774aa42240aSTomasz Figa return -EEXIST; 1775aa42240aSTomasz Figa 1776aa42240aSTomasz Figa ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 1777aa42240aSTomasz Figa "#power-domain-cells", 0, &pd_args); 1778aa42240aSTomasz Figa if (ret < 0) { 1779aa42240aSTomasz Figa if (ret != -ENOENT) 1780aa42240aSTomasz Figa return ret; 1781aa42240aSTomasz Figa 1782aa42240aSTomasz Figa /* 1783aa42240aSTomasz Figa * Try legacy Samsung-specific bindings 1784aa42240aSTomasz Figa * (for backwards compatibility of DT ABI) 1785aa42240aSTomasz Figa */ 1786aa42240aSTomasz Figa pd_args.args_count = 0; 1787aa42240aSTomasz Figa pd_args.np = of_parse_phandle(dev->of_node, 1788aa42240aSTomasz Figa "samsung,power-domain", 0); 1789aa42240aSTomasz Figa if (!pd_args.np) 1790aa42240aSTomasz Figa return -ENOENT; 1791aa42240aSTomasz Figa } 1792aa42240aSTomasz Figa 1793aa42240aSTomasz Figa pd = of_genpd_get_from_provider(&pd_args); 1794265e2cf6SEric Anholt of_node_put(pd_args.np); 1795aa42240aSTomasz Figa if (IS_ERR(pd)) { 1796aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 1797aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 1798311fa6adSJon Hunter return -EPROBE_DEFER; 1799aa42240aSTomasz Figa } 1800aa42240aSTomasz Figa 1801aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 1802aa42240aSTomasz Figa 180393af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 1804aa42240aSTomasz Figa ret = pm_genpd_add_device(pd, dev); 1805aa42240aSTomasz Figa if (ret != -EAGAIN) 1806aa42240aSTomasz Figa break; 180793af5e93SGeert Uytterhoeven 180893af5e93SGeert Uytterhoeven mdelay(i); 1809aa42240aSTomasz Figa cond_resched(); 1810aa42240aSTomasz Figa } 1811aa42240aSTomasz Figa 1812aa42240aSTomasz Figa if (ret < 0) { 1813aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 1814aa42240aSTomasz Figa pd->name, ret); 1815311fa6adSJon Hunter goto out; 1816aa42240aSTomasz Figa } 1817aa42240aSTomasz Figa 1818aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 1819632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 18207420aa4fSUlf Hansson ret = genpd_poweron(pd); 1821aa42240aSTomasz Figa 1822311fa6adSJon Hunter out: 1823311fa6adSJon Hunter return ret ? -EPROBE_DEFER : 0; 1824aa42240aSTomasz Figa } 1825aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 1826d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 18272bd5306aSMaciej Matraszek 18282bd5306aSMaciej Matraszek 18292bd5306aSMaciej Matraszek /*** debugfs support ***/ 18302bd5306aSMaciej Matraszek 18312bd5306aSMaciej Matraszek #ifdef CONFIG_PM_ADVANCED_DEBUG 18322bd5306aSMaciej Matraszek #include <linux/pm.h> 18332bd5306aSMaciej Matraszek #include <linux/device.h> 18342bd5306aSMaciej Matraszek #include <linux/debugfs.h> 18352bd5306aSMaciej Matraszek #include <linux/seq_file.h> 18362bd5306aSMaciej Matraszek #include <linux/init.h> 18372bd5306aSMaciej Matraszek #include <linux/kobject.h> 18382bd5306aSMaciej Matraszek static struct dentry *pm_genpd_debugfs_dir; 18392bd5306aSMaciej Matraszek 18402bd5306aSMaciej Matraszek /* 18412bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 1842d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 18432bd5306aSMaciej Matraszek */ 18442bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 18452bd5306aSMaciej Matraszek { 18462bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 18472bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 18482bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 18492bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 18502bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 18512bd5306aSMaciej Matraszek }; 18522bd5306aSMaciej Matraszek const char *p = ""; 18532bd5306aSMaciej Matraszek 18542bd5306aSMaciej Matraszek if (dev->power.runtime_error) 18552bd5306aSMaciej Matraszek p = "error"; 18562bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 18572bd5306aSMaciej Matraszek p = "unsupported"; 18582bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 18592bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 18602bd5306aSMaciej Matraszek else 18612bd5306aSMaciej Matraszek WARN_ON(1); 18622bd5306aSMaciej Matraszek 18632bd5306aSMaciej Matraszek seq_puts(s, p); 18642bd5306aSMaciej Matraszek } 18652bd5306aSMaciej Matraszek 18662bd5306aSMaciej Matraszek static int pm_genpd_summary_one(struct seq_file *s, 186766a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 18682bd5306aSMaciej Matraszek { 18692bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 18702bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 18712bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 18722bd5306aSMaciej Matraszek }; 18732bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 18742bd5306aSMaciej Matraszek const char *kobj_path; 18752bd5306aSMaciej Matraszek struct gpd_link *link; 18762bd5306aSMaciej Matraszek int ret; 18772bd5306aSMaciej Matraszek 187866a5ca4bSKevin Hilman ret = mutex_lock_interruptible(&genpd->lock); 18792bd5306aSMaciej Matraszek if (ret) 18802bd5306aSMaciej Matraszek return -ERESTARTSYS; 18812bd5306aSMaciej Matraszek 188266a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 18832bd5306aSMaciej Matraszek goto exit; 188466a5ca4bSKevin Hilman seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]); 18852bd5306aSMaciej Matraszek 18862bd5306aSMaciej Matraszek /* 18872bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 18882bd5306aSMaciej Matraszek * master and slave, so we are safe. 188966a5ca4bSKevin Hilman * Also genpd->name is immutable. 18902bd5306aSMaciej Matraszek */ 189166a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 18922bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 189366a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 18942bd5306aSMaciej Matraszek seq_puts(s, ", "); 18952bd5306aSMaciej Matraszek } 18962bd5306aSMaciej Matraszek 189766a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 18982bd5306aSMaciej Matraszek kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL); 18992bd5306aSMaciej Matraszek if (kobj_path == NULL) 19002bd5306aSMaciej Matraszek continue; 19012bd5306aSMaciej Matraszek 19022bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 19032bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 19042bd5306aSMaciej Matraszek kfree(kobj_path); 19052bd5306aSMaciej Matraszek } 19062bd5306aSMaciej Matraszek 19072bd5306aSMaciej Matraszek seq_puts(s, "\n"); 19082bd5306aSMaciej Matraszek exit: 190966a5ca4bSKevin Hilman mutex_unlock(&genpd->lock); 19102bd5306aSMaciej Matraszek 19112bd5306aSMaciej Matraszek return 0; 19122bd5306aSMaciej Matraszek } 19132bd5306aSMaciej Matraszek 19142bd5306aSMaciej Matraszek static int pm_genpd_summary_show(struct seq_file *s, void *data) 19152bd5306aSMaciej Matraszek { 191666a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 19172bd5306aSMaciej Matraszek int ret = 0; 19182bd5306aSMaciej Matraszek 19192bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 19202bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 19212bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 19222bd5306aSMaciej Matraszek 19232bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 19242bd5306aSMaciej Matraszek if (ret) 19252bd5306aSMaciej Matraszek return -ERESTARTSYS; 19262bd5306aSMaciej Matraszek 192766a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 192866a5ca4bSKevin Hilman ret = pm_genpd_summary_one(s, genpd); 19292bd5306aSMaciej Matraszek if (ret) 19302bd5306aSMaciej Matraszek break; 19312bd5306aSMaciej Matraszek } 19322bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 19332bd5306aSMaciej Matraszek 19342bd5306aSMaciej Matraszek return ret; 19352bd5306aSMaciej Matraszek } 19362bd5306aSMaciej Matraszek 19372bd5306aSMaciej Matraszek static int pm_genpd_summary_open(struct inode *inode, struct file *file) 19382bd5306aSMaciej Matraszek { 19392bd5306aSMaciej Matraszek return single_open(file, pm_genpd_summary_show, NULL); 19402bd5306aSMaciej Matraszek } 19412bd5306aSMaciej Matraszek 19422bd5306aSMaciej Matraszek static const struct file_operations pm_genpd_summary_fops = { 19432bd5306aSMaciej Matraszek .open = pm_genpd_summary_open, 19442bd5306aSMaciej Matraszek .read = seq_read, 19452bd5306aSMaciej Matraszek .llseek = seq_lseek, 19462bd5306aSMaciej Matraszek .release = single_release, 19472bd5306aSMaciej Matraszek }; 19482bd5306aSMaciej Matraszek 19492bd5306aSMaciej Matraszek static int __init pm_genpd_debug_init(void) 19502bd5306aSMaciej Matraszek { 19512bd5306aSMaciej Matraszek struct dentry *d; 19522bd5306aSMaciej Matraszek 19532bd5306aSMaciej Matraszek pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 19542bd5306aSMaciej Matraszek 19552bd5306aSMaciej Matraszek if (!pm_genpd_debugfs_dir) 19562bd5306aSMaciej Matraszek return -ENOMEM; 19572bd5306aSMaciej Matraszek 19582bd5306aSMaciej Matraszek d = debugfs_create_file("pm_genpd_summary", S_IRUGO, 19592bd5306aSMaciej Matraszek pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); 19602bd5306aSMaciej Matraszek if (!d) 19612bd5306aSMaciej Matraszek return -ENOMEM; 19622bd5306aSMaciej Matraszek 19632bd5306aSMaciej Matraszek return 0; 19642bd5306aSMaciej Matraszek } 19652bd5306aSMaciej Matraszek late_initcall(pm_genpd_debug_init); 19662bd5306aSMaciej Matraszek 19672bd5306aSMaciej Matraszek static void __exit pm_genpd_debug_exit(void) 19682bd5306aSMaciej Matraszek { 19692bd5306aSMaciej Matraszek debugfs_remove_recursive(pm_genpd_debugfs_dir); 19702bd5306aSMaciej Matraszek } 19712bd5306aSMaciej Matraszek __exitcall(pm_genpd_debug_exit); 19722bd5306aSMaciej Matraszek #endif /* CONFIG_PM_ADVANCED_DEBUG */ 1973