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 9f721889fSRafael J. Wysocki #include <linux/init.h> 10f721889fSRafael J. Wysocki #include <linux/kernel.h> 11f721889fSRafael J. Wysocki #include <linux/io.h> 12f721889fSRafael J. Wysocki #include <linux/pm_runtime.h> 13f721889fSRafael J. Wysocki #include <linux/pm_domain.h> 146ff7bb0dSRafael J. Wysocki #include <linux/pm_qos.h> 15f721889fSRafael J. Wysocki #include <linux/slab.h> 16f721889fSRafael J. Wysocki #include <linux/err.h> 1717b75ecaSRafael J. Wysocki #include <linux/sched.h> 1817b75ecaSRafael J. Wysocki #include <linux/suspend.h> 19d5e4cbfeSRafael J. Wysocki #include <linux/export.h> 20d5e4cbfeSRafael J. Wysocki 21d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 22d5e4cbfeSRafael J. Wysocki ({ \ 23d5e4cbfeSRafael J. Wysocki type (*__routine)(struct device *__d); \ 24d5e4cbfeSRafael J. Wysocki type __ret = (type)0; \ 25d5e4cbfeSRafael J. Wysocki \ 26d5e4cbfeSRafael J. Wysocki __routine = genpd->dev_ops.callback; \ 27d5e4cbfeSRafael J. Wysocki if (__routine) { \ 28d5e4cbfeSRafael J. Wysocki __ret = __routine(dev); \ 29d5e4cbfeSRafael J. Wysocki } else { \ 30d5e4cbfeSRafael J. Wysocki __routine = dev_gpd_data(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 370140d8bdSRafael J. Wysocki #define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name) \ 380140d8bdSRafael J. Wysocki ({ \ 390140d8bdSRafael J. Wysocki ktime_t __start = ktime_get(); \ 400140d8bdSRafael J. Wysocki type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ 410140d8bdSRafael J. Wysocki s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ 426ff7bb0dSRafael J. Wysocki struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \ 436ff7bb0dSRafael J. Wysocki if (!__retval && __elapsed > __td->field) { \ 446ff7bb0dSRafael J. Wysocki __td->field = __elapsed; \ 450140d8bdSRafael J. Wysocki dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ 460140d8bdSRafael J. Wysocki __elapsed); \ 476ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; \ 486ff7bb0dSRafael J. Wysocki __td->constraint_changed = true; \ 490140d8bdSRafael J. Wysocki } \ 500140d8bdSRafael J. Wysocki __retval; \ 510140d8bdSRafael J. Wysocki }) 520140d8bdSRafael J. Wysocki 535125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list); 545125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock); 555125bbf3SRafael J. Wysocki 568bc0251dSRafael J. Wysocki static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name) 578bc0251dSRafael J. Wysocki { 588bc0251dSRafael J. Wysocki struct generic_pm_domain *genpd = NULL, *gpd; 598bc0251dSRafael J. Wysocki 608bc0251dSRafael J. Wysocki if (IS_ERR_OR_NULL(domain_name)) 618bc0251dSRafael J. Wysocki return NULL; 628bc0251dSRafael J. Wysocki 638bc0251dSRafael J. Wysocki mutex_lock(&gpd_list_lock); 648bc0251dSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 658bc0251dSRafael J. Wysocki if (!strcmp(gpd->name, domain_name)) { 668bc0251dSRafael J. Wysocki genpd = gpd; 678bc0251dSRafael J. Wysocki break; 688bc0251dSRafael J. Wysocki } 698bc0251dSRafael J. Wysocki } 708bc0251dSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 718bc0251dSRafael J. Wysocki return genpd; 728bc0251dSRafael J. Wysocki } 738bc0251dSRafael J. Wysocki 745248051bSRafael J. Wysocki #ifdef CONFIG_PM 755248051bSRafael J. Wysocki 76b02c999aSRafael J. Wysocki struct generic_pm_domain *dev_to_genpd(struct device *dev) 775248051bSRafael J. Wysocki { 785248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 795248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 805248051bSRafael J. Wysocki 81596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 825248051bSRafael J. Wysocki } 83f721889fSRafael J. Wysocki 84d5e4cbfeSRafael J. Wysocki static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) 85d5e4cbfeSRafael J. Wysocki { 860140d8bdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev, 870140d8bdSRafael J. Wysocki stop_latency_ns, "stop"); 88d5e4cbfeSRafael J. Wysocki } 89d5e4cbfeSRafael J. Wysocki 90d5e4cbfeSRafael J. Wysocki static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) 91d5e4cbfeSRafael J. Wysocki { 920140d8bdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev, 930140d8bdSRafael J. Wysocki start_latency_ns, "start"); 94d5e4cbfeSRafael J. Wysocki } 95d5e4cbfeSRafael J. Wysocki 96c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 97f721889fSRafael J. Wysocki { 98c4bb3160SRafael J. Wysocki bool ret = false; 99c4bb3160SRafael J. Wysocki 100c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 101c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 102c4bb3160SRafael J. Wysocki 103c4bb3160SRafael J. Wysocki return ret; 104c4bb3160SRafael J. Wysocki } 105c4bb3160SRafael J. Wysocki 106c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 107c4bb3160SRafael J. Wysocki { 108c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 109c4bb3160SRafael J. Wysocki smp_mb__after_atomic_inc(); 110f721889fSRafael J. Wysocki } 111f721889fSRafael J. Wysocki 11217b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd) 11317b75ecaSRafael J. Wysocki { 11417b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 11517b75ecaSRafael J. Wysocki 11617b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 11717b75ecaSRafael J. Wysocki /* 11817b75ecaSRafael J. Wysocki * Wait for the domain to transition into either the active, 11917b75ecaSRafael J. Wysocki * or the power off state. 12017b75ecaSRafael J. Wysocki */ 12117b75ecaSRafael J. Wysocki for (;;) { 12217b75ecaSRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 12317b75ecaSRafael J. Wysocki TASK_UNINTERRUPTIBLE); 124c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 125c6d22b37SRafael J. Wysocki || genpd->status == GPD_STATE_POWER_OFF) 12617b75ecaSRafael J. Wysocki break; 12717b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 12817b75ecaSRafael J. Wysocki 12917b75ecaSRafael J. Wysocki schedule(); 13017b75ecaSRafael J. Wysocki 13117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 13217b75ecaSRafael J. Wysocki } 13317b75ecaSRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 13417b75ecaSRafael J. Wysocki } 13517b75ecaSRafael J. Wysocki 13617b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd) 13717b75ecaSRafael J. Wysocki { 13817b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 13917b75ecaSRafael J. Wysocki } 14017b75ecaSRafael J. Wysocki 141c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd) 142c6d22b37SRafael J. Wysocki { 143c6d22b37SRafael J. Wysocki if (genpd->resume_count == 0) 144c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 145c6d22b37SRafael J. Wysocki } 146c6d22b37SRafael J. Wysocki 147cbc9ef02SRafael J. Wysocki static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) 148cbc9ef02SRafael J. Wysocki { 149cbc9ef02SRafael J. Wysocki s64 usecs64; 150cbc9ef02SRafael J. Wysocki 151cbc9ef02SRafael J. Wysocki if (!genpd->cpu_data) 152cbc9ef02SRafael J. Wysocki return; 153cbc9ef02SRafael J. Wysocki 154cbc9ef02SRafael J. Wysocki usecs64 = genpd->power_on_latency_ns; 155cbc9ef02SRafael J. Wysocki do_div(usecs64, NSEC_PER_USEC); 156cbc9ef02SRafael J. Wysocki usecs64 += genpd->cpu_data->saved_exit_latency; 157cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->exit_latency = usecs64; 158cbc9ef02SRafael J. Wysocki } 159cbc9ef02SRafael J. Wysocki 160f721889fSRafael J. Wysocki /** 1615063ce15SRafael J. Wysocki * __pm_genpd_poweron - Restore power to a given PM domain and its masters. 1625248051bSRafael J. Wysocki * @genpd: PM domain to power up. 1635248051bSRafael J. Wysocki * 1645063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 1655248051bSRafael J. Wysocki * resume a device belonging to it. 1665248051bSRafael J. Wysocki */ 1678951ef02SSachin Kamat static int __pm_genpd_poweron(struct generic_pm_domain *genpd) 1683f241775SRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 1695248051bSRafael J. Wysocki { 1705063ce15SRafael J. Wysocki struct gpd_link *link; 1713f241775SRafael J. Wysocki DEFINE_WAIT(wait); 1725248051bSRafael J. Wysocki int ret = 0; 1735248051bSRafael J. Wysocki 1745063ce15SRafael J. Wysocki /* If the domain's master is being waited for, we have to wait too. */ 1753f241775SRafael J. Wysocki for (;;) { 1763f241775SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 1773f241775SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 17817877eb5SRafael J. Wysocki if (genpd->status != GPD_STATE_WAIT_MASTER) 1793f241775SRafael J. Wysocki break; 1803f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 1813f241775SRafael J. Wysocki 1823f241775SRafael J. Wysocki schedule(); 1833f241775SRafael J. Wysocki 18417b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 1853f241775SRafael J. Wysocki } 1863f241775SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 18717b75ecaSRafael J. Wysocki 18817b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 189596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1903f241775SRafael J. Wysocki return 0; 1915248051bSRafael J. Wysocki 192c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 193c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 1943f241775SRafael J. Wysocki return 0; 195c6d22b37SRafael J. Wysocki } 196c6d22b37SRafael J. Wysocki 197cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 198cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 199cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = true; 200cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 201cbc9ef02SRafael J. Wysocki goto out; 202cbc9ef02SRafael J. Wysocki } 203cbc9ef02SRafael J. Wysocki 2045063ce15SRafael J. Wysocki /* 2055063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 2065063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 2075063ce15SRafael J. Wysocki * with it. 2085063ce15SRafael J. Wysocki */ 2095063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 2105063ce15SRafael J. Wysocki genpd_sd_counter_inc(link->master); 21117877eb5SRafael J. Wysocki genpd->status = GPD_STATE_WAIT_MASTER; 2123c07cbc4SRafael J. Wysocki 2135248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 2145248051bSRafael J. Wysocki 2155063ce15SRafael J. Wysocki ret = pm_genpd_poweron(link->master); 2169e08cf42SRafael J. Wysocki 2179e08cf42SRafael J. Wysocki mutex_lock(&genpd->lock); 2189e08cf42SRafael J. Wysocki 2193f241775SRafael J. Wysocki /* 2203f241775SRafael J. Wysocki * The "wait for parent" status is guaranteed not to change 2215063ce15SRafael J. Wysocki * while the master is powering on. 2223f241775SRafael J. Wysocki */ 2233f241775SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 2243f241775SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 2255063ce15SRafael J. Wysocki if (ret) { 2265063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2279e08cf42SRafael J. Wysocki goto err; 2285248051bSRafael J. Wysocki } 2295063ce15SRafael J. Wysocki } 2305248051bSRafael J. Wysocki 2319e08cf42SRafael J. Wysocki if (genpd->power_on) { 2320140d8bdSRafael J. Wysocki ktime_t time_start = ktime_get(); 2330140d8bdSRafael J. Wysocki s64 elapsed_ns; 2340140d8bdSRafael J. Wysocki 235fe202fdeSRafael J. Wysocki ret = genpd->power_on(genpd); 2369e08cf42SRafael J. Wysocki if (ret) 2379e08cf42SRafael J. Wysocki goto err; 2380140d8bdSRafael J. Wysocki 2390140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 240e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_on_latency_ns) { 2410140d8bdSRafael J. Wysocki genpd->power_on_latency_ns = elapsed_ns; 2426ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 243cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 244e84b2c20SRafael J. Wysocki if (genpd->name) 245e84b2c20SRafael J. Wysocki pr_warning("%s: Power-on latency exceeded, " 246e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 247e84b2c20SRafael J. Wysocki elapsed_ns); 248e84b2c20SRafael J. Wysocki } 2493c07cbc4SRafael J. Wysocki } 2505248051bSRafael J. Wysocki 251cbc9ef02SRafael J. Wysocki out: 2529e08cf42SRafael J. Wysocki genpd_set_active(genpd); 2539e08cf42SRafael J. Wysocki 2543f241775SRafael J. Wysocki return 0; 2559e08cf42SRafael J. Wysocki 2569e08cf42SRafael J. Wysocki err: 2575063ce15SRafael J. Wysocki list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) 2585063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2599e08cf42SRafael J. Wysocki 2603f241775SRafael J. Wysocki return ret; 2613f241775SRafael J. Wysocki } 2623f241775SRafael J. Wysocki 2633f241775SRafael J. Wysocki /** 2645063ce15SRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its masters. 2653f241775SRafael J. Wysocki * @genpd: PM domain to power up. 2663f241775SRafael J. Wysocki */ 2673f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd) 2683f241775SRafael J. Wysocki { 2693f241775SRafael J. Wysocki int ret; 2703f241775SRafael J. Wysocki 2713f241775SRafael J. Wysocki mutex_lock(&genpd->lock); 2723f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 2733f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 2743f241775SRafael J. Wysocki return ret; 2755248051bSRafael J. Wysocki } 2765248051bSRafael J. Wysocki 2778bc0251dSRafael J. Wysocki /** 2788bc0251dSRafael J. Wysocki * pm_genpd_name_poweron - Restore power to a given PM domain and its masters. 2798bc0251dSRafael J. Wysocki * @domain_name: Name of the PM domain to power up. 2808bc0251dSRafael J. Wysocki */ 2818bc0251dSRafael J. Wysocki int pm_genpd_name_poweron(const char *domain_name) 2828bc0251dSRafael J. Wysocki { 2838bc0251dSRafael J. Wysocki struct generic_pm_domain *genpd; 2848bc0251dSRafael J. Wysocki 2858bc0251dSRafael J. Wysocki genpd = pm_genpd_lookup_name(domain_name); 2868bc0251dSRafael J. Wysocki return genpd ? pm_genpd_poweron(genpd) : -EINVAL; 2878bc0251dSRafael J. Wysocki } 2888bc0251dSRafael J. Wysocki 2895248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 2905248051bSRafael J. Wysocki 2915248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 2925248051bSRafael J. Wysocki 293b3d3b9fbSSachin Kamat static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd, 294b3d3b9fbSSachin Kamat struct device *dev) 295b3d3b9fbSSachin Kamat { 296b3d3b9fbSSachin Kamat return GENPD_DEV_CALLBACK(genpd, int, start, dev); 297b3d3b9fbSSachin Kamat } 298b3d3b9fbSSachin Kamat 2998e9afafdSRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) 3008e9afafdSRafael J. Wysocki { 3018e9afafdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, 3028e9afafdSRafael J. Wysocki save_state_latency_ns, "state save"); 3038e9afafdSRafael J. Wysocki } 3048e9afafdSRafael J. Wysocki 3058e9afafdSRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) 3068e9afafdSRafael J. Wysocki { 3078e9afafdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, 3088e9afafdSRafael J. Wysocki restore_state_latency_ns, 3098e9afafdSRafael J. Wysocki "state restore"); 3108e9afafdSRafael J. Wysocki } 3118e9afafdSRafael J. Wysocki 3126ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 3136ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 3146ff7bb0dSRafael J. Wysocki { 3156ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 3166ff7bb0dSRafael J. Wysocki struct device *dev; 3176ff7bb0dSRafael J. Wysocki 3186ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 3196ff7bb0dSRafael J. Wysocki 3206ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 3216ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 3226ff7bb0dSRafael J. Wysocki if (!dev) { 3236ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 3246ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 3256ff7bb0dSRafael J. Wysocki } 3266ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 3276ff7bb0dSRafael J. Wysocki 3286ff7bb0dSRafael J. Wysocki for (;;) { 3296ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 3306ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 3316ff7bb0dSRafael J. Wysocki 3326ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 3336ff7bb0dSRafael J. Wysocki 3346ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 3356ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 3361d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 3376ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 3386ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 3396ff7bb0dSRafael J. Wysocki } else { 3406ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 3416ff7bb0dSRafael J. Wysocki } 3426ff7bb0dSRafael J. Wysocki 3436ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 3446ff7bb0dSRafael J. Wysocki 3456ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 3466ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 3476ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 3486ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 3496ff7bb0dSRafael J. Wysocki } 3506ff7bb0dSRafael J. Wysocki 3516ff7bb0dSRafael J. Wysocki dev = dev->parent; 3526ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 3536ff7bb0dSRafael J. Wysocki break; 3546ff7bb0dSRafael J. Wysocki } 3556ff7bb0dSRafael J. Wysocki 3566ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 3576ff7bb0dSRafael J. Wysocki } 3586ff7bb0dSRafael J. Wysocki 3595248051bSRafael J. Wysocki /** 360f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 3614605ab65SRafael J. Wysocki * @pdd: Domain data of the device to save the state of. 362f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 363f721889fSRafael J. Wysocki */ 3644605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd, 365f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 36617b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 367f721889fSRafael J. Wysocki { 368cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3694605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 370f721889fSRafael J. Wysocki int ret = 0; 371f721889fSRafael J. Wysocki 372cd0ea672SRafael J. Wysocki if (gpd_data->need_restore) 373f721889fSRafael J. Wysocki return 0; 374f721889fSRafael J. Wysocki 37517b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 37617b75ecaSRafael J. Wysocki 377d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 378ecf00475SRafael J. Wysocki ret = genpd_save_dev(genpd, dev); 379d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 380f721889fSRafael J. Wysocki 38117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 38217b75ecaSRafael J. Wysocki 383f721889fSRafael J. Wysocki if (!ret) 384cd0ea672SRafael J. Wysocki gpd_data->need_restore = true; 385f721889fSRafael J. Wysocki 386f721889fSRafael J. Wysocki return ret; 387f721889fSRafael J. Wysocki } 388f721889fSRafael J. Wysocki 389f721889fSRafael J. Wysocki /** 390f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 3914605ab65SRafael J. Wysocki * @pdd: Domain data of the device to restore the state of. 392f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 393f721889fSRafael J. Wysocki */ 3944605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd, 395f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 39617b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 397f721889fSRafael J. Wysocki { 398cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3994605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 40080de3d7fSRafael J. Wysocki bool need_restore = gpd_data->need_restore; 401f721889fSRafael J. Wysocki 40280de3d7fSRafael J. Wysocki gpd_data->need_restore = false; 40317b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 40417b75ecaSRafael J. Wysocki 405d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 40680de3d7fSRafael J. Wysocki if (need_restore) 407ecf00475SRafael J. Wysocki genpd_restore_dev(genpd, dev); 408f721889fSRafael J. Wysocki 40917b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 410f721889fSRafael J. Wysocki } 411f721889fSRafael J. Wysocki 412f721889fSRafael J. Wysocki /** 413c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 414c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 415c6d22b37SRafael J. Wysocki * 416c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 417c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 418c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 419c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 420c6d22b37SRafael J. Wysocki */ 421c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 422c6d22b37SRafael J. Wysocki { 42317877eb5SRafael J. Wysocki return genpd->status == GPD_STATE_WAIT_MASTER 4243f241775SRafael J. Wysocki || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 425c6d22b37SRafael J. Wysocki } 426c6d22b37SRafael J. Wysocki 427c6d22b37SRafael J. Wysocki /** 42856375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 42956375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 43056375fd4SRafael J. Wysocki * 43156375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 43256375fd4SRafael J. Wysocki * before. 43356375fd4SRafael J. Wysocki */ 4340bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 43556375fd4SRafael J. Wysocki { 43656375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 43756375fd4SRafael J. Wysocki } 43856375fd4SRafael J. Wysocki 43956375fd4SRafael J. Wysocki /** 440f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 441f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 442f721889fSRafael J. Wysocki * 443f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 444f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 445f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 446f721889fSRafael J. Wysocki */ 447f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 44817b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 449f721889fSRafael J. Wysocki { 4504605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 4515063ce15SRafael J. Wysocki struct gpd_link *link; 452f721889fSRafael J. Wysocki unsigned int not_suspended; 453c6d22b37SRafael J. Wysocki int ret = 0; 454f721889fSRafael J. Wysocki 455c6d22b37SRafael J. Wysocki start: 456c6d22b37SRafael J. Wysocki /* 457c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 458c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 4595063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 460c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 4613f241775SRafael J. Wysocki * (4) System suspend is in progress. 462c6d22b37SRafael J. Wysocki */ 4633f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 46417877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 4653f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 466f721889fSRafael J. Wysocki return 0; 467f721889fSRafael J. Wysocki 468c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 469f721889fSRafael J. Wysocki return -EBUSY; 470f721889fSRafael J. Wysocki 471f721889fSRafael J. Wysocki not_suspended = 0; 47234b1f762SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) { 47334b1f762SRafael J. Wysocki enum pm_qos_flags_status stat; 47434b1f762SRafael J. Wysocki 47534b1f762SRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, 47634b1f762SRafael J. Wysocki PM_QOS_FLAG_NO_POWER_OFF 47734b1f762SRafael J. Wysocki | PM_QOS_FLAG_REMOTE_WAKEUP); 47834b1f762SRafael J. Wysocki if (stat > PM_QOS_FLAGS_NONE) 47934b1f762SRafael J. Wysocki return -EBUSY; 48034b1f762SRafael J. Wysocki 4810aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 482feb70af0SRafael J. Wysocki || pdd->dev->power.irq_safe)) 483f721889fSRafael J. Wysocki not_suspended++; 48434b1f762SRafael J. Wysocki } 485f721889fSRafael J. Wysocki 486f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 487f721889fSRafael J. Wysocki return -EBUSY; 488f721889fSRafael J. Wysocki 489c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 490c6d22b37SRafael J. Wysocki /* 491c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 492c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 493c6d22b37SRafael J. Wysocki */ 494c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 495c6d22b37SRafael J. Wysocki return 0; 496c6d22b37SRafael J. Wysocki } 497c6d22b37SRafael J. Wysocki 498f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 499f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 500f721889fSRafael J. Wysocki return -EAGAIN; 501f721889fSRafael J. Wysocki } 502f721889fSRafael J. Wysocki 50317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 504c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 50517b75ecaSRafael J. Wysocki 5064605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 5073c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 5084605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 5093f241775SRafael J. Wysocki 5103f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 5113f241775SRafael J. Wysocki goto out; 5123f241775SRafael J. Wysocki 513697a7f37SRafael J. Wysocki if (ret) { 514697a7f37SRafael J. Wysocki genpd_set_active(genpd); 515697a7f37SRafael J. Wysocki goto out; 516697a7f37SRafael J. Wysocki } 517f721889fSRafael J. Wysocki 518c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 519c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 520c6d22b37SRafael J. Wysocki goto start; 521c6d22b37SRafael J. Wysocki } 522c6d22b37SRafael J. Wysocki } 52317b75ecaSRafael J. Wysocki 524cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 525cbc9ef02SRafael J. Wysocki /* 526cbc9ef02SRafael J. Wysocki * If cpu_data is set, cpuidle should turn the domain off when 527cbc9ef02SRafael J. Wysocki * the CPU in it is idle. In that case we don't decrement the 528cbc9ef02SRafael J. Wysocki * subdomain counts of the master domains, so that power is not 529cbc9ef02SRafael J. Wysocki * removed from the current domain prematurely as a result of 530cbc9ef02SRafael J. Wysocki * cutting off the masters' power. 531cbc9ef02SRafael J. Wysocki */ 532cbc9ef02SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 533cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 534cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = false; 535cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 536cbc9ef02SRafael J. Wysocki goto out; 537cbc9ef02SRafael J. Wysocki } 538cbc9ef02SRafael J. Wysocki 5393c07cbc4SRafael J. Wysocki if (genpd->power_off) { 5400140d8bdSRafael J. Wysocki ktime_t time_start; 5410140d8bdSRafael J. Wysocki s64 elapsed_ns; 5420140d8bdSRafael J. Wysocki 5433c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 5443c07cbc4SRafael J. Wysocki ret = -EBUSY; 545c6d22b37SRafael J. Wysocki goto out; 546c6d22b37SRafael J. Wysocki } 54717b75ecaSRafael J. Wysocki 5480140d8bdSRafael J. Wysocki time_start = ktime_get(); 5490140d8bdSRafael J. Wysocki 5503c07cbc4SRafael J. Wysocki /* 5515063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 5525063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 5533c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 5543c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 5553c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 5563c07cbc4SRafael J. Wysocki * happen very often). 5573c07cbc4SRafael J. Wysocki */ 558d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 559d2805402SRafael J. Wysocki if (ret == -EBUSY) { 560d2805402SRafael J. Wysocki genpd_set_active(genpd); 561d2805402SRafael J. Wysocki goto out; 562d2805402SRafael J. Wysocki } 5630140d8bdSRafael J. Wysocki 5640140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 565e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_off_latency_ns) { 5660140d8bdSRafael J. Wysocki genpd->power_off_latency_ns = elapsed_ns; 5676ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 568e84b2c20SRafael J. Wysocki if (genpd->name) 569e84b2c20SRafael J. Wysocki pr_warning("%s: Power-off latency exceeded, " 570e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 571e84b2c20SRafael J. Wysocki elapsed_ns); 572e84b2c20SRafael J. Wysocki } 573d2805402SRafael J. Wysocki } 574f721889fSRafael J. Wysocki 57517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 576221e9b58SRafael J. Wysocki 5775063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5785063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 5795063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 5805063ce15SRafael J. Wysocki } 58117b75ecaSRafael J. Wysocki 582c6d22b37SRafael J. Wysocki out: 583c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 584c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 585c6d22b37SRafael J. Wysocki return ret; 586f721889fSRafael J. Wysocki } 587f721889fSRafael J. Wysocki 588f721889fSRafael J. Wysocki /** 589f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 590f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 591f721889fSRafael J. Wysocki */ 592f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 593f721889fSRafael J. Wysocki { 594f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 595f721889fSRafael J. Wysocki 596f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 597f721889fSRafael J. Wysocki 59817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 599f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 60017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 601f721889fSRafael J. Wysocki } 602f721889fSRafael J. Wysocki 603f721889fSRafael J. Wysocki /** 604f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 605f721889fSRafael J. Wysocki * @dev: Device to suspend. 606f721889fSRafael J. Wysocki * 607f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 608f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 609f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 610f721889fSRafael J. Wysocki */ 611f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 612f721889fSRafael J. Wysocki { 613f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 614b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 615d5e4cbfeSRafael J. Wysocki int ret; 616f721889fSRafael J. Wysocki 617f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 618f721889fSRafael J. Wysocki 6195248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6205248051bSRafael J. Wysocki if (IS_ERR(genpd)) 621f721889fSRafael J. Wysocki return -EINVAL; 622f721889fSRafael J. Wysocki 6230aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 6240aa2a221SRafael J. Wysocki 625b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 626b02c999aSRafael J. Wysocki if (stop_ok && !stop_ok(dev)) 627b02c999aSRafael J. Wysocki return -EBUSY; 628b02c999aSRafael J. Wysocki 629d5e4cbfeSRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 630f721889fSRafael J. Wysocki if (ret) 63117b75ecaSRafael J. Wysocki return ret; 63217b75ecaSRafael J. Wysocki 6330aa2a221SRafael J. Wysocki /* 6340aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 6350aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 6360aa2a221SRafael J. Wysocki */ 6370aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 6380aa2a221SRafael J. Wysocki return 0; 6390aa2a221SRafael J. Wysocki 640c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 641f721889fSRafael J. Wysocki genpd->in_progress++; 642f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 643f721889fSRafael J. Wysocki genpd->in_progress--; 644c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 645f721889fSRafael J. Wysocki 646f721889fSRafael J. Wysocki return 0; 647f721889fSRafael J. Wysocki } 648f721889fSRafael J. Wysocki 649f721889fSRafael J. Wysocki /** 650f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 651f721889fSRafael J. Wysocki * @dev: Device to resume. 652f721889fSRafael J. Wysocki * 653f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 654f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 655f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 656f721889fSRafael J. Wysocki */ 657f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 658f721889fSRafael J. Wysocki { 659f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 660c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 661f721889fSRafael J. Wysocki int ret; 662f721889fSRafael J. Wysocki 663f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 664f721889fSRafael J. Wysocki 6655248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6665248051bSRafael J. Wysocki if (IS_ERR(genpd)) 667f721889fSRafael J. Wysocki return -EINVAL; 668f721889fSRafael J. Wysocki 6690aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 6700aa2a221SRafael J. Wysocki 6710aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 6720aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 673e2e3e4e5SRafael J. Wysocki return genpd_start_dev_no_timing(genpd, dev); 6740aa2a221SRafael J. Wysocki 675c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 6763f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 6773f241775SRafael J. Wysocki if (ret) { 6783f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 6793f241775SRafael J. Wysocki return ret; 6803f241775SRafael J. Wysocki } 68117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 682c6d22b37SRafael J. Wysocki genpd->resume_count++; 683c6d22b37SRafael J. Wysocki for (;;) { 684c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 685c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 686c6d22b37SRafael J. Wysocki /* 687c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 688c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 689c6d22b37SRafael J. Wysocki * not wait. 690c6d22b37SRafael J. Wysocki */ 691c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 692c6d22b37SRafael J. Wysocki break; 693c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 694c6d22b37SRafael J. Wysocki 695c6d22b37SRafael J. Wysocki schedule(); 696c6d22b37SRafael J. Wysocki 697c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 698c6d22b37SRafael J. Wysocki } 699c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 700cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 701c6d22b37SRafael J. Wysocki genpd->resume_count--; 702c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 70317b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 704c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 70517b75ecaSRafael J. Wysocki 706f721889fSRafael J. Wysocki return 0; 707f721889fSRafael J. Wysocki } 708f721889fSRafael J. Wysocki 70917f2ae7fSRafael J. Wysocki /** 71017f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 71117f2ae7fSRafael J. Wysocki */ 71217f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 71317f2ae7fSRafael J. Wysocki { 71417f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 71517f2ae7fSRafael J. Wysocki 71617f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 71717f2ae7fSRafael J. Wysocki 71817f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 71917f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 72017f2ae7fSRafael J. Wysocki 72117f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 72217f2ae7fSRafael J. Wysocki } 72317f2ae7fSRafael J. Wysocki 724f721889fSRafael J. Wysocki #else 725f721889fSRafael J. Wysocki 7266ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 7276ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 7286ff7bb0dSRafael J. Wysocki { 7296ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 7306ff7bb0dSRafael J. Wysocki } 7316ff7bb0dSRafael J. Wysocki 732f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 733f721889fSRafael J. Wysocki 734f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 735f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 736f721889fSRafael J. Wysocki 737f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 738f721889fSRafael J. Wysocki 739596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 740596ba34bSRafael J. Wysocki 74177f827deSRafael J. Wysocki /** 74277f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 74377f827deSRafael J. Wysocki * @genpd: PM domain to check. 74477f827deSRafael J. Wysocki */ 74577f827deSRafael J. Wysocki static bool pm_genpd_present(struct generic_pm_domain *genpd) 74677f827deSRafael J. Wysocki { 74777f827deSRafael J. Wysocki struct generic_pm_domain *gpd; 74877f827deSRafael J. Wysocki 74977f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 75077f827deSRafael J. Wysocki return false; 75177f827deSRafael J. Wysocki 75277f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 75377f827deSRafael J. Wysocki if (gpd == genpd) 75477f827deSRafael J. Wysocki return true; 75577f827deSRafael J. Wysocki 75677f827deSRafael J. Wysocki return false; 75777f827deSRafael J. Wysocki } 75877f827deSRafael J. Wysocki 759d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 760d5e4cbfeSRafael J. Wysocki struct device *dev) 761d5e4cbfeSRafael J. Wysocki { 762d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 763d5e4cbfeSRafael J. Wysocki } 764d5e4cbfeSRafael J. Wysocki 765d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) 766d23b9b00SRafael J. Wysocki { 767d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); 768d23b9b00SRafael J. Wysocki } 769d23b9b00SRafael J. Wysocki 770d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) 771d23b9b00SRafael J. Wysocki { 772d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); 773d23b9b00SRafael J. Wysocki } 774d23b9b00SRafael J. Wysocki 775d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) 776d23b9b00SRafael J. Wysocki { 777d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); 778d23b9b00SRafael J. Wysocki } 779d23b9b00SRafael J. Wysocki 780d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) 781d23b9b00SRafael J. Wysocki { 782d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume, dev); 783d23b9b00SRafael J. Wysocki } 784d23b9b00SRafael J. Wysocki 785d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) 786d23b9b00SRafael J. Wysocki { 787d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); 788d23b9b00SRafael J. Wysocki } 789d23b9b00SRafael J. Wysocki 790d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) 791d23b9b00SRafael J. Wysocki { 792d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); 793d23b9b00SRafael J. Wysocki } 794d23b9b00SRafael J. Wysocki 795d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) 796d23b9b00SRafael J. Wysocki { 797d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); 798d23b9b00SRafael J. Wysocki } 799d23b9b00SRafael J. Wysocki 800d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) 801d23b9b00SRafael J. Wysocki { 802d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); 803d23b9b00SRafael J. Wysocki } 804d23b9b00SRafael J. Wysocki 805596ba34bSRafael J. Wysocki /** 8065063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 807596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 808596ba34bSRafael J. Wysocki * 809596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 8105063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 811596ba34bSRafael J. Wysocki * 81277f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 81377f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 81477f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 81577f827deSRafael J. Wysocki * parallel). 816596ba34bSRafael J. Wysocki */ 817596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 818596ba34bSRafael J. Wysocki { 8195063ce15SRafael J. Wysocki struct gpd_link *link; 820596ba34bSRafael J. Wysocki 82117b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 822596ba34bSRafael J. Wysocki return; 823596ba34bSRafael J. Wysocki 824c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 825c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 826596ba34bSRafael J. Wysocki return; 827596ba34bSRafael J. Wysocki 828596ba34bSRafael J. Wysocki if (genpd->power_off) 829596ba34bSRafael J. Wysocki genpd->power_off(genpd); 830596ba34bSRafael J. Wysocki 83117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 8325063ce15SRafael J. Wysocki 8335063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 8345063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 8355063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 836596ba34bSRafael J. Wysocki } 837596ba34bSRafael J. Wysocki } 838596ba34bSRafael J. Wysocki 839596ba34bSRafael J. Wysocki /** 840802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 841802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 842802d8b49SRafael J. Wysocki * 84377f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 84477f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 84577f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 84677f827deSRafael J. Wysocki * parallel). 847802d8b49SRafael J. Wysocki */ 848802d8b49SRafael J. Wysocki static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) 849802d8b49SRafael J. Wysocki { 850802d8b49SRafael J. Wysocki struct gpd_link *link; 851802d8b49SRafael J. Wysocki 852802d8b49SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) 853802d8b49SRafael J. Wysocki return; 854802d8b49SRafael J. Wysocki 855802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 856802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(link->master); 857802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 858802d8b49SRafael J. Wysocki } 859802d8b49SRafael J. Wysocki 860802d8b49SRafael J. Wysocki if (genpd->power_on) 861802d8b49SRafael J. Wysocki genpd->power_on(genpd); 862802d8b49SRafael J. Wysocki 863802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 864802d8b49SRafael J. Wysocki } 865802d8b49SRafael J. Wysocki 866802d8b49SRafael J. Wysocki /** 8674ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 8684ecd6e65SRafael J. Wysocki * @dev: Device to check. 8694ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 8704ecd6e65SRafael J. Wysocki * 8714ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 8724ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 8734ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 8744ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 8754ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 8764ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 8774ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8784ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8794ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8804ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8814ecd6e65SRafael J. Wysocki */ 8824ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 8834ecd6e65SRafael J. Wysocki { 8844ecd6e65SRafael J. Wysocki bool active_wakeup; 8854ecd6e65SRafael J. Wysocki 8864ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8874ecd6e65SRafael J. Wysocki return false; 8884ecd6e65SRafael J. Wysocki 889d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8904ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8914ecd6e65SRafael J. Wysocki } 8924ecd6e65SRafael J. Wysocki 8934ecd6e65SRafael J. Wysocki /** 894596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 895596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 896596ba34bSRafael J. Wysocki * 897596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 898596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 899596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 900596ba34bSRafael J. Wysocki * consisting of I/O devices. 901596ba34bSRafael J. Wysocki */ 902596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 903596ba34bSRafael J. Wysocki { 904596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 905b6c10c84SRafael J. Wysocki int ret; 906596ba34bSRafael J. Wysocki 907596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 908596ba34bSRafael J. Wysocki 909596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 910596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 911596ba34bSRafael J. Wysocki return -EINVAL; 912596ba34bSRafael J. Wysocki 91317b75ecaSRafael J. Wysocki /* 91417b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 91517b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 91617b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 91717b75ecaSRafael J. Wysocki */ 91817b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 91917b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 92017b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 92117b75ecaSRafael J. Wysocki 92217b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 92317b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 92417b75ecaSRafael J. Wysocki return -EBUSY; 92517b75ecaSRafael J. Wysocki } 92617b75ecaSRafael J. Wysocki 9274ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 9284ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 9294ecd6e65SRafael J. Wysocki 93017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 931596ba34bSRafael J. Wysocki 93265533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 93365533bbfSRafael J. Wysocki genpd->suspended_count = 0; 93417b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 93565533bbfSRafael J. Wysocki } 93617b75ecaSRafael J. Wysocki 93717b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 938596ba34bSRafael J. Wysocki 939596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 94017b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 941596ba34bSRafael J. Wysocki return 0; 942596ba34bSRafael J. Wysocki } 943596ba34bSRafael J. Wysocki 944596ba34bSRafael J. Wysocki /* 94517b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 94617b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 947d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 94817b75ecaSRafael J. Wysocki * to make it operational. 949596ba34bSRafael J. Wysocki */ 95017b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 951596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 952596ba34bSRafael J. Wysocki 953b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 954b6c10c84SRafael J. Wysocki if (ret) { 955b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 956b6c10c84SRafael J. Wysocki 957b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 958b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 959b6c10c84SRafael J. Wysocki 960b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 96117b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 962b6c10c84SRafael J. Wysocki } 96317b75ecaSRafael J. Wysocki 96417b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 965b6c10c84SRafael J. Wysocki return ret; 966596ba34bSRafael J. Wysocki } 967596ba34bSRafael J. Wysocki 968596ba34bSRafael J. Wysocki /** 969596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 970596ba34bSRafael J. Wysocki * @dev: Device to suspend. 971596ba34bSRafael J. Wysocki * 972596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 973596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 974596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 975596ba34bSRafael J. Wysocki */ 976596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 977596ba34bSRafael J. Wysocki { 978596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 979596ba34bSRafael J. Wysocki 980596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 981596ba34bSRafael J. Wysocki 982596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 983596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 984596ba34bSRafael J. Wysocki return -EINVAL; 985596ba34bSRafael J. Wysocki 986d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); 987596ba34bSRafael J. Wysocki } 988596ba34bSRafael J. Wysocki 989596ba34bSRafael J. Wysocki /** 9900496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 991596ba34bSRafael J. Wysocki * @dev: Device to suspend. 992596ba34bSRafael J. Wysocki * 993596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 994596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 995596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 996596ba34bSRafael J. Wysocki */ 9970496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 998596ba34bSRafael J. Wysocki { 999596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1000596ba34bSRafael J. Wysocki 1001596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1002596ba34bSRafael J. Wysocki 1003596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1004596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1005596ba34bSRafael J. Wysocki return -EINVAL; 1006596ba34bSRafael J. Wysocki 10070496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); 10080496c8aeSRafael J. Wysocki } 1009596ba34bSRafael J. Wysocki 10100496c8aeSRafael J. Wysocki /** 10110496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 10120496c8aeSRafael J. Wysocki * @dev: Device to suspend. 10130496c8aeSRafael J. Wysocki * 10140496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 10150496c8aeSRafael J. Wysocki * been stopped. 10160496c8aeSRafael J. Wysocki */ 10170496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 10180496c8aeSRafael J. Wysocki { 10190496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1020596ba34bSRafael J. Wysocki 10210496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10220496c8aeSRafael J. Wysocki 10230496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10240496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10250496c8aeSRafael J. Wysocki return -EINVAL; 10260496c8aeSRafael J. Wysocki 1027dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 10280496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 1029d4f2d87aSRafael J. Wysocki return 0; 1030d4f2d87aSRafael J. Wysocki 1031d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 1032596ba34bSRafael J. Wysocki 1033596ba34bSRafael J. Wysocki /* 1034596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1035596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1036596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 1037596ba34bSRafael J. Wysocki */ 1038596ba34bSRafael J. Wysocki genpd->suspended_count++; 1039596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 1040596ba34bSRafael J. Wysocki 1041596ba34bSRafael J. Wysocki return 0; 1042596ba34bSRafael J. Wysocki } 1043596ba34bSRafael J. Wysocki 1044596ba34bSRafael J. Wysocki /** 10450496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 1046596ba34bSRafael J. Wysocki * @dev: Device to resume. 1047596ba34bSRafael J. Wysocki * 10480496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 1049596ba34bSRafael J. Wysocki */ 1050596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 1051596ba34bSRafael J. Wysocki { 1052596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1053596ba34bSRafael J. Wysocki 1054596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1055596ba34bSRafael J. Wysocki 1056596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1057596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1058596ba34bSRafael J. Wysocki return -EINVAL; 1059596ba34bSRafael J. Wysocki 1060dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 1061cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 1062596ba34bSRafael J. Wysocki return 0; 1063596ba34bSRafael J. Wysocki 1064596ba34bSRafael J. Wysocki /* 1065596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1066596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1067596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 1068596ba34bSRafael J. Wysocki */ 1069802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(genpd); 1070596ba34bSRafael J. Wysocki genpd->suspended_count--; 1071596ba34bSRafael J. Wysocki 10720496c8aeSRafael J. Wysocki return genpd_start_dev(genpd, dev); 1073596ba34bSRafael J. Wysocki } 1074596ba34bSRafael J. Wysocki 1075596ba34bSRafael J. Wysocki /** 10760496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 10770496c8aeSRafael J. Wysocki * @dev: Device to resume. 10780496c8aeSRafael J. Wysocki * 10790496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 10800496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10810496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10820496c8aeSRafael J. Wysocki * devices. 10830496c8aeSRafael J. Wysocki */ 10840496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 10850496c8aeSRafael J. Wysocki { 10860496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10870496c8aeSRafael J. Wysocki 10880496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10890496c8aeSRafael J. Wysocki 10900496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10910496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10920496c8aeSRafael J. Wysocki return -EINVAL; 10930496c8aeSRafael J. Wysocki 10940496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); 10950496c8aeSRafael J. Wysocki } 10960496c8aeSRafael J. Wysocki 10970496c8aeSRafael J. Wysocki /** 10980496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 1099596ba34bSRafael J. Wysocki * @dev: Device to resume. 1100596ba34bSRafael J. Wysocki * 1101596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 1102596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1103596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1104596ba34bSRafael J. Wysocki */ 1105596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 1106596ba34bSRafael J. Wysocki { 1107596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1108596ba34bSRafael J. Wysocki 1109596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1110596ba34bSRafael J. Wysocki 1111596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1112596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1113596ba34bSRafael J. Wysocki return -EINVAL; 1114596ba34bSRafael J. Wysocki 1115d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); 1116596ba34bSRafael J. Wysocki } 1117596ba34bSRafael J. Wysocki 1118596ba34bSRafael J. Wysocki /** 11190496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 1120596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1121596ba34bSRafael J. Wysocki * 1122596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 1123596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1124596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1125596ba34bSRafael J. Wysocki */ 1126596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 1127596ba34bSRafael J. Wysocki { 1128596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1129596ba34bSRafael J. Wysocki 1130596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1131596ba34bSRafael J. Wysocki 1132596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1133596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1134596ba34bSRafael J. Wysocki return -EINVAL; 1135596ba34bSRafael J. Wysocki 1136d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); 1137596ba34bSRafael J. Wysocki } 1138596ba34bSRafael J. Wysocki 1139596ba34bSRafael J. Wysocki /** 11400496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 11410496c8aeSRafael J. Wysocki * @dev: Device to freeze. 11420496c8aeSRafael J. Wysocki * 11430496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 11440496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 11450496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 11460496c8aeSRafael J. Wysocki * devices. 11470496c8aeSRafael J. Wysocki */ 11480496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 11490496c8aeSRafael J. Wysocki { 11500496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 11510496c8aeSRafael J. Wysocki 11520496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 11530496c8aeSRafael J. Wysocki 11540496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11550496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11560496c8aeSRafael J. Wysocki return -EINVAL; 11570496c8aeSRafael J. Wysocki 11580496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); 11590496c8aeSRafael J. Wysocki } 11600496c8aeSRafael J. Wysocki 11610496c8aeSRafael J. Wysocki /** 11620496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1163596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1164596ba34bSRafael J. Wysocki * 1165596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1166596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1167596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1168596ba34bSRafael J. Wysocki * devices. 1169596ba34bSRafael J. Wysocki */ 1170596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1171596ba34bSRafael J. Wysocki { 1172596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1173596ba34bSRafael J. Wysocki 1174596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1175596ba34bSRafael J. Wysocki 1176596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1177596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1178596ba34bSRafael J. Wysocki return -EINVAL; 1179596ba34bSRafael J. Wysocki 1180dbf37414SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); 1181596ba34bSRafael J. Wysocki } 1182596ba34bSRafael J. Wysocki 1183596ba34bSRafael J. Wysocki /** 11840496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1185596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1186596ba34bSRafael J. Wysocki * 11870496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 11880496c8aeSRafael J. Wysocki * before the system transition. 1189596ba34bSRafael J. Wysocki */ 1190596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1191596ba34bSRafael J. Wysocki { 1192596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1193596ba34bSRafael J. Wysocki 1194596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1195596ba34bSRafael J. Wysocki 1196596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1197596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1198596ba34bSRafael J. Wysocki return -EINVAL; 1199596ba34bSRafael J. Wysocki 1200dbf37414SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); 12010496c8aeSRafael J. Wysocki } 1202596ba34bSRafael J. Wysocki 12030496c8aeSRafael J. Wysocki /** 12040496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 12050496c8aeSRafael J. Wysocki * @dev: Device to thaw. 12060496c8aeSRafael J. Wysocki * 12070496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 12080496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 12090496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 12100496c8aeSRafael J. Wysocki * devices. 12110496c8aeSRafael J. Wysocki */ 12120496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 12130496c8aeSRafael J. Wysocki { 12140496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1215596ba34bSRafael J. Wysocki 12160496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 12170496c8aeSRafael J. Wysocki 12180496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 12190496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 12200496c8aeSRafael J. Wysocki return -EINVAL; 12210496c8aeSRafael J. Wysocki 12220496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); 1223596ba34bSRafael J. Wysocki } 1224596ba34bSRafael J. Wysocki 1225596ba34bSRafael J. Wysocki /** 1226596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1227596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1228596ba34bSRafael J. Wysocki * 1229596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1230596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1231596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1232596ba34bSRafael J. Wysocki */ 1233596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1234596ba34bSRafael J. Wysocki { 1235596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1236596ba34bSRafael J. Wysocki 1237596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1238596ba34bSRafael J. Wysocki 1239596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1240596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1241596ba34bSRafael J. Wysocki return -EINVAL; 1242596ba34bSRafael J. Wysocki 1243d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); 1244596ba34bSRafael J. Wysocki } 1245596ba34bSRafael J. Wysocki 1246596ba34bSRafael J. Wysocki /** 12470496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1248596ba34bSRafael J. Wysocki * @dev: Device to resume. 1249596ba34bSRafael J. Wysocki * 12500496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 12510496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1252596ba34bSRafael J. Wysocki */ 1253596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1254596ba34bSRafael J. Wysocki { 1255596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1256596ba34bSRafael J. Wysocki 1257596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1258596ba34bSRafael J. Wysocki 1259596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1260596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1261596ba34bSRafael J. Wysocki return -EINVAL; 1262596ba34bSRafael J. Wysocki 1263596ba34bSRafael J. Wysocki /* 1264596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1265596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1266596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 126765533bbfSRafael J. Wysocki * 126865533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 126965533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 127065533bbfSRafael J. Wysocki */ 127165533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 127265533bbfSRafael J. Wysocki /* 127365533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1274802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 1275802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1276596ba34bSRafael J. Wysocki */ 127717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1278596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1279596ba34bSRafael J. Wysocki /* 128065533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 128165533bbfSRafael J. Wysocki * sure it will be off going forward. 1282596ba34bSRafael J. Wysocki */ 1283596ba34bSRafael J. Wysocki if (genpd->power_off) 1284596ba34bSRafael J. Wysocki genpd->power_off(genpd); 128565533bbfSRafael J. Wysocki 1286596ba34bSRafael J. Wysocki return 0; 1287596ba34bSRafael J. Wysocki } 128865533bbfSRafael J. Wysocki } 1289596ba34bSRafael J. Wysocki 129018dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 129118dd2eceSRafael J. Wysocki return 0; 129218dd2eceSRafael J. Wysocki 1293802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(genpd); 1294596ba34bSRafael J. Wysocki 1295dbf37414SRafael J. Wysocki return genpd_start_dev(genpd, dev); 1296596ba34bSRafael J. Wysocki } 1297596ba34bSRafael J. Wysocki 1298596ba34bSRafael J. Wysocki /** 1299596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1300596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1301596ba34bSRafael J. Wysocki * 1302596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1303596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1304596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1305596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1306596ba34bSRafael J. Wysocki */ 1307596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1308596ba34bSRafael J. Wysocki { 1309596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1310596ba34bSRafael J. Wysocki bool run_complete; 1311596ba34bSRafael J. Wysocki 1312596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1313596ba34bSRafael J. Wysocki 1314596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1315596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1316596ba34bSRafael J. Wysocki return; 1317596ba34bSRafael J. Wysocki 1318596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1319596ba34bSRafael J. Wysocki 1320596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1321596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1322596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1323596ba34bSRafael J. Wysocki 1324596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1325596ba34bSRafael J. Wysocki 1326596ba34bSRafael J. Wysocki if (run_complete) { 1327596ba34bSRafael J. Wysocki pm_generic_complete(dev); 13286f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1329596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 1330af939339SUlf Hansson pm_request_idle(dev); 1331596ba34bSRafael J. Wysocki } 1332596ba34bSRafael J. Wysocki } 1333596ba34bSRafael J. Wysocki 133477f827deSRafael J. Wysocki /** 133577f827deSRafael J. Wysocki * pm_genpd_syscore_switch - Switch power during system core suspend or resume. 133677f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 133777f827deSRafael J. Wysocki * 133877f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 133977f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 134077f827deSRafael J. Wysocki */ 134177f827deSRafael J. Wysocki void pm_genpd_syscore_switch(struct device *dev, bool suspend) 134277f827deSRafael J. Wysocki { 134377f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 134477f827deSRafael J. Wysocki 134577f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 134677f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 134777f827deSRafael J. Wysocki return; 134877f827deSRafael J. Wysocki 134977f827deSRafael J. Wysocki if (suspend) { 135077f827deSRafael J. Wysocki genpd->suspended_count++; 135177f827deSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 135277f827deSRafael J. Wysocki } else { 135377f827deSRafael J. Wysocki pm_genpd_sync_poweron(genpd); 135477f827deSRafael J. Wysocki genpd->suspended_count--; 135577f827deSRafael J. Wysocki } 135677f827deSRafael J. Wysocki } 135777f827deSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch); 135877f827deSRafael J. Wysocki 1359596ba34bSRafael J. Wysocki #else 1360596ba34bSRafael J. Wysocki 1361596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1362596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 13630496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1364596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 13650496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1366596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1367596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1368596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 13690496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1370596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 13710496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1372596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1373596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1374596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1375596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1376596ba34bSRafael J. Wysocki 1377596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1378596ba34bSRafael J. Wysocki 13791d5fcfecSRafael J. Wysocki static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) 13801d5fcfecSRafael J. Wysocki { 13811d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13821d5fcfecSRafael J. Wysocki 13831d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 13841d5fcfecSRafael J. Wysocki if (!gpd_data) 13851d5fcfecSRafael J. Wysocki return NULL; 13861d5fcfecSRafael J. Wysocki 13871d5fcfecSRafael J. Wysocki mutex_init(&gpd_data->lock); 13881d5fcfecSRafael J. Wysocki gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 13891d5fcfecSRafael J. Wysocki dev_pm_qos_add_notifier(dev, &gpd_data->nb); 13901d5fcfecSRafael J. Wysocki return gpd_data; 13911d5fcfecSRafael J. Wysocki } 13921d5fcfecSRafael J. Wysocki 13931d5fcfecSRafael J. Wysocki static void __pm_genpd_free_dev_data(struct device *dev, 13941d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 13951d5fcfecSRafael J. Wysocki { 13961d5fcfecSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13971d5fcfecSRafael J. Wysocki kfree(gpd_data); 13981d5fcfecSRafael J. Wysocki } 13991d5fcfecSRafael J. Wysocki 1400f721889fSRafael J. Wysocki /** 1401b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1402f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1403f721889fSRafael J. Wysocki * @dev: Device to be added. 1404b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1405f721889fSRafael J. Wysocki */ 1406b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1407b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1408f721889fSRafael J. Wysocki { 14091d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; 14104605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1411f721889fSRafael J. Wysocki int ret = 0; 1412f721889fSRafael J. Wysocki 1413f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1414f721889fSRafael J. Wysocki 1415f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1416f721889fSRafael J. Wysocki return -EINVAL; 1417f721889fSRafael J. Wysocki 14181d5fcfecSRafael J. Wysocki gpd_data_new = __pm_genpd_alloc_dev_data(dev); 14191d5fcfecSRafael J. Wysocki if (!gpd_data_new) 14206ff7bb0dSRafael J. Wysocki return -ENOMEM; 14216ff7bb0dSRafael J. Wysocki 142217b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1423f721889fSRafael J. Wysocki 1424596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1425596ba34bSRafael J. Wysocki ret = -EAGAIN; 1426596ba34bSRafael J. Wysocki goto out; 1427596ba34bSRafael J. Wysocki } 1428596ba34bSRafael J. Wysocki 14294605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 14304605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1431f721889fSRafael J. Wysocki ret = -EINVAL; 1432f721889fSRafael J. Wysocki goto out; 1433f721889fSRafael J. Wysocki } 1434f721889fSRafael J. Wysocki 14351d5fcfecSRafael J. Wysocki ret = dev_pm_get_subsys_data(dev); 14361d5fcfecSRafael J. Wysocki if (ret) 14371d5fcfecSRafael J. Wysocki goto out; 14381d5fcfecSRafael J. Wysocki 1439596ba34bSRafael J. Wysocki genpd->device_count++; 14406ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1441f721889fSRafael J. Wysocki 14426ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 14431d5fcfecSRafael J. Wysocki 14446ff7bb0dSRafael J. Wysocki dev->pm_domain = &genpd->domain; 14451d5fcfecSRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 14461d5fcfecSRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 14471d5fcfecSRafael J. Wysocki } else { 14481d5fcfecSRafael J. Wysocki gpd_data = gpd_data_new; 1449cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 14501d5fcfecSRafael J. Wysocki } 14511d5fcfecSRafael J. Wysocki gpd_data->refcount++; 1452b02c999aSRafael J. Wysocki if (td) 1453b02c999aSRafael J. Wysocki gpd_data->td = *td; 1454f721889fSRafael J. Wysocki 14551d5fcfecSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 14561d5fcfecSRafael J. Wysocki 14571d5fcfecSRafael J. Wysocki mutex_lock(&gpd_data->lock); 14581d5fcfecSRafael J. Wysocki gpd_data->base.dev = dev; 14591d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 14601d5fcfecSRafael J. Wysocki gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 14616ff7bb0dSRafael J. Wysocki gpd_data->td.constraint_changed = true; 14626ff7bb0dSRafael J. Wysocki gpd_data->td.effective_constraint_ns = -1; 14636ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 14646ff7bb0dSRafael J. Wysocki 1465f721889fSRafael J. Wysocki out: 146617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1467f721889fSRafael J. Wysocki 14681d5fcfecSRafael J. Wysocki if (gpd_data != gpd_data_new) 14691d5fcfecSRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data_new); 14701d5fcfecSRafael J. Wysocki 1471f721889fSRafael J. Wysocki return ret; 1472f721889fSRafael J. Wysocki } 1473f721889fSRafael J. Wysocki 1474f721889fSRafael J. Wysocki /** 1475c8aa130bSThomas Abraham * __pm_genpd_of_add_device - Add a device to an I/O PM domain. 1476c8aa130bSThomas Abraham * @genpd_node: Device tree node pointer representing a PM domain to which the 1477c8aa130bSThomas Abraham * the device is added to. 1478c8aa130bSThomas Abraham * @dev: Device to be added. 1479c8aa130bSThomas Abraham * @td: Set of PM QoS timing parameters to attach to the device. 1480c8aa130bSThomas Abraham */ 1481c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, 1482c8aa130bSThomas Abraham struct gpd_timing_data *td) 1483c8aa130bSThomas Abraham { 1484c8aa130bSThomas Abraham struct generic_pm_domain *genpd = NULL, *gpd; 1485c8aa130bSThomas Abraham 1486c8aa130bSThomas Abraham dev_dbg(dev, "%s()\n", __func__); 1487c8aa130bSThomas Abraham 1488c8aa130bSThomas Abraham if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) 1489c8aa130bSThomas Abraham return -EINVAL; 1490c8aa130bSThomas Abraham 1491c8aa130bSThomas Abraham mutex_lock(&gpd_list_lock); 1492c8aa130bSThomas Abraham list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1493c8aa130bSThomas Abraham if (gpd->of_node == genpd_node) { 1494c8aa130bSThomas Abraham genpd = gpd; 1495c8aa130bSThomas Abraham break; 1496c8aa130bSThomas Abraham } 1497c8aa130bSThomas Abraham } 1498c8aa130bSThomas Abraham mutex_unlock(&gpd_list_lock); 1499c8aa130bSThomas Abraham 1500c8aa130bSThomas Abraham if (!genpd) 1501c8aa130bSThomas Abraham return -EINVAL; 1502c8aa130bSThomas Abraham 1503c8aa130bSThomas Abraham return __pm_genpd_add_device(genpd, dev, td); 1504c8aa130bSThomas Abraham } 1505c8aa130bSThomas Abraham 1506b5abb085SRafael J. Wysocki 1507b5abb085SRafael J. Wysocki /** 1508b5abb085SRafael J. Wysocki * __pm_genpd_name_add_device - Find I/O PM domain and add a device to it. 1509b5abb085SRafael J. Wysocki * @domain_name: Name of the PM domain to add the device to. 1510b5abb085SRafael J. Wysocki * @dev: Device to be added. 1511b5abb085SRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1512b5abb085SRafael J. Wysocki */ 1513b5abb085SRafael J. Wysocki int __pm_genpd_name_add_device(const char *domain_name, struct device *dev, 1514b5abb085SRafael J. Wysocki struct gpd_timing_data *td) 1515b5abb085SRafael J. Wysocki { 15168bc0251dSRafael J. Wysocki return __pm_genpd_add_device(pm_genpd_lookup_name(domain_name), dev, td); 1517b5abb085SRafael J. Wysocki } 1518b5abb085SRafael J. Wysocki 1519c8aa130bSThomas Abraham /** 1520f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1521f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1522f721889fSRafael J. Wysocki * @dev: Device to be removed. 1523f721889fSRafael J. Wysocki */ 1524f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1525f721889fSRafael J. Wysocki struct device *dev) 1526f721889fSRafael J. Wysocki { 15276ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 15284605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 15291d5fcfecSRafael J. Wysocki bool remove = false; 1530efa69025SRafael J. Wysocki int ret = 0; 1531f721889fSRafael J. Wysocki 1532f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1533f721889fSRafael J. Wysocki 1534efa69025SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) 1535efa69025SRafael J. Wysocki || IS_ERR_OR_NULL(dev->pm_domain) 1536efa69025SRafael J. Wysocki || pd_to_genpd(dev->pm_domain) != genpd) 1537f721889fSRafael J. Wysocki return -EINVAL; 1538f721889fSRafael J. Wysocki 153917b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1540f721889fSRafael J. Wysocki 1541596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1542596ba34bSRafael J. Wysocki ret = -EAGAIN; 1543596ba34bSRafael J. Wysocki goto out; 1544596ba34bSRafael J. Wysocki } 1545596ba34bSRafael J. Wysocki 15466ff7bb0dSRafael J. Wysocki genpd->device_count--; 15476ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 15486ff7bb0dSRafael J. Wysocki 15496ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 15501d5fcfecSRafael J. Wysocki 1551f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1552efa69025SRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1553efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 15541d5fcfecSRafael J. Wysocki gpd_data = to_gpd_data(pdd); 15551d5fcfecSRafael J. Wysocki if (--gpd_data->refcount == 0) { 1556efa69025SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 15571d5fcfecSRafael J. Wysocki remove = true; 15581d5fcfecSRafael J. Wysocki } 15591d5fcfecSRafael J. Wysocki 15606ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1561f721889fSRafael J. Wysocki 15626ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 15636ff7bb0dSRafael J. Wysocki pdd->dev = NULL; 15646ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 15656ff7bb0dSRafael J. Wysocki 15666ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 15676ff7bb0dSRafael J. Wysocki 15686ff7bb0dSRafael J. Wysocki dev_pm_put_subsys_data(dev); 15691d5fcfecSRafael J. Wysocki if (remove) 15701d5fcfecSRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data); 15711d5fcfecSRafael J. Wysocki 15726ff7bb0dSRafael J. Wysocki return 0; 1573f721889fSRafael J. Wysocki 1574596ba34bSRafael J. Wysocki out: 157517b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1576f721889fSRafael J. Wysocki 1577f721889fSRafael J. Wysocki return ret; 1578f721889fSRafael J. Wysocki } 1579f721889fSRafael J. Wysocki 1580f721889fSRafael J. Wysocki /** 1581ca1d72f0SRafael J. Wysocki * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. 1582ca1d72f0SRafael J. Wysocki * @dev: Device to set/unset the flag for. 1583ca1d72f0SRafael J. Wysocki * @val: The new value of the device's "need restore" flag. 1584ca1d72f0SRafael J. Wysocki */ 1585ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val) 1586ca1d72f0SRafael J. Wysocki { 1587ca1d72f0SRafael J. Wysocki struct pm_subsys_data *psd; 1588ca1d72f0SRafael J. Wysocki unsigned long flags; 1589ca1d72f0SRafael J. Wysocki 1590ca1d72f0SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 1591ca1d72f0SRafael J. Wysocki 1592ca1d72f0SRafael J. Wysocki psd = dev_to_psd(dev); 1593ca1d72f0SRafael J. Wysocki if (psd && psd->domain_data) 1594ca1d72f0SRafael J. Wysocki to_gpd_data(psd->domain_data)->need_restore = val; 1595ca1d72f0SRafael J. Wysocki 1596ca1d72f0SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 1597ca1d72f0SRafael J. Wysocki } 1598ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); 1599ca1d72f0SRafael J. Wysocki 1600ca1d72f0SRafael J. Wysocki /** 1601f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1602f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1603bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1604f721889fSRafael J. Wysocki */ 1605f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1606bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1607f721889fSRafael J. Wysocki { 16085063ce15SRafael J. Wysocki struct gpd_link *link; 1609f721889fSRafael J. Wysocki int ret = 0; 1610f721889fSRafael J. Wysocki 1611fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1612fb7268beSRafael J. Wysocki || genpd == subdomain) 1613f721889fSRafael J. Wysocki return -EINVAL; 1614f721889fSRafael J. Wysocki 161517b75ecaSRafael J. Wysocki start: 161617b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1617bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1618f721889fSRafael J. Wysocki 1619bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1620bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1621bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 162217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 162317b75ecaSRafael J. Wysocki goto start; 162417b75ecaSRafael J. Wysocki } 162517b75ecaSRafael J. Wysocki 162617b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1627bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1628f721889fSRafael J. Wysocki ret = -EINVAL; 1629f721889fSRafael J. Wysocki goto out; 1630f721889fSRafael J. Wysocki } 1631f721889fSRafael J. Wysocki 16324fcac10dSHuang Ying list_for_each_entry(link, &genpd->master_links, master_node) { 1633bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1634f721889fSRafael J. Wysocki ret = -EINVAL; 1635f721889fSRafael J. Wysocki goto out; 1636f721889fSRafael J. Wysocki } 1637f721889fSRafael J. Wysocki } 1638f721889fSRafael J. Wysocki 16395063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 16405063ce15SRafael J. Wysocki if (!link) { 16415063ce15SRafael J. Wysocki ret = -ENOMEM; 16425063ce15SRafael J. Wysocki goto out; 16435063ce15SRafael J. Wysocki } 16445063ce15SRafael J. Wysocki link->master = genpd; 16455063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1646bc0403ffSRafael J. Wysocki link->slave = subdomain; 1647bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1648bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1649c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1650f721889fSRafael J. Wysocki 1651f721889fSRafael J. Wysocki out: 1652bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 165317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1654f721889fSRafael J. Wysocki 1655f721889fSRafael J. Wysocki return ret; 1656f721889fSRafael J. Wysocki } 1657f721889fSRafael J. Wysocki 1658f721889fSRafael J. Wysocki /** 1659fb7268beSRafael J. Wysocki * pm_genpd_add_subdomain_names - Add a subdomain to an I/O PM domain. 1660fb7268beSRafael J. Wysocki * @master_name: Name of the master PM domain to add the subdomain to. 1661fb7268beSRafael J. Wysocki * @subdomain_name: Name of the subdomain to be added. 1662fb7268beSRafael J. Wysocki */ 1663fb7268beSRafael J. Wysocki int pm_genpd_add_subdomain_names(const char *master_name, 1664fb7268beSRafael J. Wysocki const char *subdomain_name) 1665fb7268beSRafael J. Wysocki { 1666fb7268beSRafael J. Wysocki struct generic_pm_domain *master = NULL, *subdomain = NULL, *gpd; 1667fb7268beSRafael J. Wysocki 1668fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(master_name) || IS_ERR_OR_NULL(subdomain_name)) 1669fb7268beSRafael J. Wysocki return -EINVAL; 1670fb7268beSRafael J. Wysocki 1671fb7268beSRafael J. Wysocki mutex_lock(&gpd_list_lock); 1672fb7268beSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1673fb7268beSRafael J. Wysocki if (!master && !strcmp(gpd->name, master_name)) 1674fb7268beSRafael J. Wysocki master = gpd; 1675fb7268beSRafael J. Wysocki 1676fb7268beSRafael J. Wysocki if (!subdomain && !strcmp(gpd->name, subdomain_name)) 1677fb7268beSRafael J. Wysocki subdomain = gpd; 1678fb7268beSRafael J. Wysocki 1679fb7268beSRafael J. Wysocki if (master && subdomain) 1680fb7268beSRafael J. Wysocki break; 1681fb7268beSRafael J. Wysocki } 1682fb7268beSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 1683fb7268beSRafael J. Wysocki 1684fb7268beSRafael J. Wysocki return pm_genpd_add_subdomain(master, subdomain); 1685fb7268beSRafael J. Wysocki } 1686fb7268beSRafael J. Wysocki 1687fb7268beSRafael J. Wysocki /** 1688f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1689f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 16905063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1691f721889fSRafael J. Wysocki */ 1692f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 16935063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1694f721889fSRafael J. Wysocki { 16955063ce15SRafael J. Wysocki struct gpd_link *link; 1696f721889fSRafael J. Wysocki int ret = -EINVAL; 1697f721889fSRafael J. Wysocki 16985063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1699f721889fSRafael J. Wysocki return -EINVAL; 1700f721889fSRafael J. Wysocki 170117b75ecaSRafael J. Wysocki start: 170217b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1703f721889fSRafael J. Wysocki 17045063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 17055063ce15SRafael J. Wysocki if (link->slave != subdomain) 1706f721889fSRafael J. Wysocki continue; 1707f721889fSRafael J. Wysocki 1708f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1709f721889fSRafael J. Wysocki 171017b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 171117b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 171217b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 171317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 171417b75ecaSRafael J. Wysocki goto start; 171517b75ecaSRafael J. Wysocki } 171617b75ecaSRafael J. Wysocki 17175063ce15SRafael J. Wysocki list_del(&link->master_node); 17185063ce15SRafael J. Wysocki list_del(&link->slave_node); 17195063ce15SRafael J. Wysocki kfree(link); 172017b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1721f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1722f721889fSRafael J. Wysocki 1723f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1724f721889fSRafael J. Wysocki 1725f721889fSRafael J. Wysocki ret = 0; 1726f721889fSRafael J. Wysocki break; 1727f721889fSRafael J. Wysocki } 1728f721889fSRafael J. Wysocki 172917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1730f721889fSRafael J. Wysocki 1731f721889fSRafael J. Wysocki return ret; 1732f721889fSRafael J. Wysocki } 1733f721889fSRafael J. Wysocki 1734f721889fSRafael J. Wysocki /** 1735d5e4cbfeSRafael J. Wysocki * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. 1736d5e4cbfeSRafael J. Wysocki * @dev: Device to add the callbacks to. 1737d5e4cbfeSRafael J. Wysocki * @ops: Set of callbacks to add. 1738b02c999aSRafael J. Wysocki * @td: Timing data to add to the device along with the callbacks (optional). 173962d44902SRafael J. Wysocki * 174062d44902SRafael J. Wysocki * Every call to this routine should be balanced with a call to 174162d44902SRafael J. Wysocki * __pm_genpd_remove_callbacks() and they must not be nested. 1742d5e4cbfeSRafael J. Wysocki */ 1743b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, 1744b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1745d5e4cbfeSRafael J. Wysocki { 174662d44902SRafael J. Wysocki struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; 1747d5e4cbfeSRafael J. Wysocki int ret = 0; 1748d5e4cbfeSRafael J. Wysocki 174962d44902SRafael J. Wysocki if (!(dev && ops)) 1750d5e4cbfeSRafael J. Wysocki return -EINVAL; 1751d5e4cbfeSRafael J. Wysocki 175262d44902SRafael J. Wysocki gpd_data_new = __pm_genpd_alloc_dev_data(dev); 175362d44902SRafael J. Wysocki if (!gpd_data_new) 175462d44902SRafael J. Wysocki return -ENOMEM; 175562d44902SRafael J. Wysocki 1756d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1757d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1758d5e4cbfeSRafael J. Wysocki 175962d44902SRafael J. Wysocki ret = dev_pm_get_subsys_data(dev); 176062d44902SRafael J. Wysocki if (ret) 176162d44902SRafael J. Wysocki goto out; 1762d5e4cbfeSRafael J. Wysocki 176362d44902SRafael J. Wysocki spin_lock_irq(&dev->power.lock); 176462d44902SRafael J. Wysocki 176562d44902SRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 176662d44902SRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 176762d44902SRafael J. Wysocki } else { 176862d44902SRafael J. Wysocki gpd_data = gpd_data_new; 176962d44902SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 177062d44902SRafael J. Wysocki } 177162d44902SRafael J. Wysocki gpd_data->refcount++; 1772d5e4cbfeSRafael J. Wysocki gpd_data->ops = *ops; 1773b02c999aSRafael J. Wysocki if (td) 1774b02c999aSRafael J. Wysocki gpd_data->td = *td; 1775d5e4cbfeSRafael J. Wysocki 177662d44902SRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 177762d44902SRafael J. Wysocki 177862d44902SRafael J. Wysocki out: 1779d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1780d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1781d5e4cbfeSRafael J. Wysocki 178262d44902SRafael J. Wysocki if (gpd_data != gpd_data_new) 178362d44902SRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data_new); 178462d44902SRafael J. Wysocki 1785d5e4cbfeSRafael J. Wysocki return ret; 1786d5e4cbfeSRafael J. Wysocki } 1787d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); 1788d5e4cbfeSRafael J. Wysocki 1789d5e4cbfeSRafael J. Wysocki /** 1790b02c999aSRafael J. Wysocki * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. 1791d5e4cbfeSRafael J. Wysocki * @dev: Device to remove the callbacks from. 1792b02c999aSRafael J. Wysocki * @clear_td: If set, clear the device's timing data too. 179362d44902SRafael J. Wysocki * 179462d44902SRafael J. Wysocki * This routine can only be called after pm_genpd_add_callbacks(). 1795d5e4cbfeSRafael J. Wysocki */ 1796b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) 1797d5e4cbfeSRafael J. Wysocki { 179862d44902SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = NULL; 179962d44902SRafael J. Wysocki bool remove = false; 1800d5e4cbfeSRafael J. Wysocki int ret = 0; 1801d5e4cbfeSRafael J. Wysocki 1802d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data)) 1803d5e4cbfeSRafael J. Wysocki return -EINVAL; 1804d5e4cbfeSRafael J. Wysocki 1805d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1806d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1807d5e4cbfeSRafael J. Wysocki 180862d44902SRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1809d5e4cbfeSRafael J. Wysocki 181062d44902SRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 181162d44902SRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 1812db79e53dSSachin Kamat gpd_data->ops = (struct gpd_dev_ops){ NULL }; 1813b02c999aSRafael J. Wysocki if (clear_td) 1814b02c999aSRafael J. Wysocki gpd_data->td = (struct gpd_timing_data){ 0 }; 181562d44902SRafael J. Wysocki 181662d44902SRafael J. Wysocki if (--gpd_data->refcount == 0) { 181762d44902SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 181862d44902SRafael J. Wysocki remove = true; 181962d44902SRafael J. Wysocki } 1820d5e4cbfeSRafael J. Wysocki } else { 1821d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1822d5e4cbfeSRafael J. Wysocki } 1823d5e4cbfeSRafael J. Wysocki 182462d44902SRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 182562d44902SRafael J. Wysocki 1826d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1827d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1828d5e4cbfeSRafael J. Wysocki 182962d44902SRafael J. Wysocki if (ret) 1830d5e4cbfeSRafael J. Wysocki return ret; 183162d44902SRafael J. Wysocki 183262d44902SRafael J. Wysocki dev_pm_put_subsys_data(dev); 183362d44902SRafael J. Wysocki if (remove) 183462d44902SRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data); 183562d44902SRafael J. Wysocki 183662d44902SRafael J. Wysocki return 0; 1837d5e4cbfeSRafael J. Wysocki } 1838b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); 1839d5e4cbfeSRafael J. Wysocki 184040114447SRafael J. Wysocki /** 184140114447SRafael J. Wysocki * pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle. 184240114447SRafael J. Wysocki * @genpd: PM domain to be connected with cpuidle. 184340114447SRafael J. Wysocki * @state: cpuidle state this domain can disable/enable. 184440114447SRafael J. Wysocki * 184540114447SRafael J. Wysocki * Make a PM domain behave as though it contained a CPU core, that is, instead 184640114447SRafael J. Wysocki * of calling its power down routine it will enable the given cpuidle state so 184740114447SRafael J. Wysocki * that the cpuidle subsystem can power it down (if possible and desirable). 184840114447SRafael J. Wysocki */ 184940114447SRafael J. Wysocki int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) 1850cbc9ef02SRafael J. Wysocki { 1851cbc9ef02SRafael J. Wysocki struct cpuidle_driver *cpuidle_drv; 1852cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1853cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1854cbc9ef02SRafael J. Wysocki int ret = 0; 1855cbc9ef02SRafael J. Wysocki 1856cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || state < 0) 1857cbc9ef02SRafael J. Wysocki return -EINVAL; 1858cbc9ef02SRafael J. Wysocki 1859cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1860cbc9ef02SRafael J. Wysocki 1861cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 1862cbc9ef02SRafael J. Wysocki ret = -EEXIST; 1863cbc9ef02SRafael J. Wysocki goto out; 1864cbc9ef02SRafael J. Wysocki } 1865cbc9ef02SRafael J. Wysocki cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); 1866cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1867cbc9ef02SRafael J. Wysocki ret = -ENOMEM; 1868cbc9ef02SRafael J. Wysocki goto out; 1869cbc9ef02SRafael J. Wysocki } 1870cbc9ef02SRafael J. Wysocki cpuidle_drv = cpuidle_driver_ref(); 1871cbc9ef02SRafael J. Wysocki if (!cpuidle_drv) { 1872cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1873debe081aSjhbird.choi@samsung.com goto err_drv; 1874cbc9ef02SRafael J. Wysocki } 1875cbc9ef02SRafael J. Wysocki if (cpuidle_drv->state_count <= state) { 1876cbc9ef02SRafael J. Wysocki ret = -EINVAL; 1877cbc9ef02SRafael J. Wysocki goto err; 1878cbc9ef02SRafael J. Wysocki } 1879cbc9ef02SRafael J. Wysocki idle_state = &cpuidle_drv->states[state]; 1880cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1881cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1882cbc9ef02SRafael J. Wysocki goto err; 1883cbc9ef02SRafael J. Wysocki } 1884cbc9ef02SRafael J. Wysocki cpu_data->idle_state = idle_state; 1885cbc9ef02SRafael J. Wysocki cpu_data->saved_exit_latency = idle_state->exit_latency; 1886cbc9ef02SRafael J. Wysocki genpd->cpu_data = cpu_data; 1887cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 1888cbc9ef02SRafael J. Wysocki 1889cbc9ef02SRafael J. Wysocki out: 1890cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1891cbc9ef02SRafael J. Wysocki return ret; 1892cbc9ef02SRafael J. Wysocki 1893cbc9ef02SRafael J. Wysocki err: 1894cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1895debe081aSjhbird.choi@samsung.com 1896debe081aSjhbird.choi@samsung.com err_drv: 1897debe081aSjhbird.choi@samsung.com kfree(cpu_data); 1898cbc9ef02SRafael J. Wysocki goto out; 1899cbc9ef02SRafael J. Wysocki } 1900cbc9ef02SRafael J. Wysocki 190140114447SRafael J. Wysocki /** 190274a2799aSRafael J. Wysocki * pm_genpd_name_attach_cpuidle - Find PM domain and connect cpuidle to it. 190374a2799aSRafael J. Wysocki * @name: Name of the domain to connect to cpuidle. 190474a2799aSRafael J. Wysocki * @state: cpuidle state this domain can manipulate. 190574a2799aSRafael J. Wysocki */ 190674a2799aSRafael J. Wysocki int pm_genpd_name_attach_cpuidle(const char *name, int state) 190774a2799aSRafael J. Wysocki { 190874a2799aSRafael J. Wysocki return pm_genpd_attach_cpuidle(pm_genpd_lookup_name(name), state); 190974a2799aSRafael J. Wysocki } 191074a2799aSRafael J. Wysocki 191174a2799aSRafael J. Wysocki /** 191240114447SRafael J. Wysocki * pm_genpd_detach_cpuidle - Remove the cpuidle connection from a PM domain. 191340114447SRafael J. Wysocki * @genpd: PM domain to remove the cpuidle connection from. 191440114447SRafael J. Wysocki * 191540114447SRafael J. Wysocki * Remove the cpuidle connection set up by pm_genpd_attach_cpuidle() from the 191640114447SRafael J. Wysocki * given PM domain. 191740114447SRafael J. Wysocki */ 191840114447SRafael J. Wysocki int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) 1919cbc9ef02SRafael J. Wysocki { 1920cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1921cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1922cbc9ef02SRafael J. Wysocki int ret = 0; 1923cbc9ef02SRafael J. Wysocki 1924cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1925cbc9ef02SRafael J. Wysocki return -EINVAL; 1926cbc9ef02SRafael J. Wysocki 1927cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1928cbc9ef02SRafael J. Wysocki 1929cbc9ef02SRafael J. Wysocki cpu_data = genpd->cpu_data; 1930cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1931cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1932cbc9ef02SRafael J. Wysocki goto out; 1933cbc9ef02SRafael J. Wysocki } 1934cbc9ef02SRafael J. Wysocki idle_state = cpu_data->idle_state; 1935cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1936cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1937cbc9ef02SRafael J. Wysocki goto out; 1938cbc9ef02SRafael J. Wysocki } 1939cbc9ef02SRafael J. Wysocki idle_state->exit_latency = cpu_data->saved_exit_latency; 1940cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1941cbc9ef02SRafael J. Wysocki genpd->cpu_data = NULL; 1942cbc9ef02SRafael J. Wysocki kfree(cpu_data); 1943cbc9ef02SRafael J. Wysocki 1944cbc9ef02SRafael J. Wysocki out: 1945cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1946cbc9ef02SRafael J. Wysocki return ret; 1947cbc9ef02SRafael J. Wysocki } 1948cbc9ef02SRafael J. Wysocki 194974a2799aSRafael J. Wysocki /** 195074a2799aSRafael J. Wysocki * pm_genpd_name_detach_cpuidle - Find PM domain and disconnect cpuidle from it. 195174a2799aSRafael J. Wysocki * @name: Name of the domain to disconnect cpuidle from. 195274a2799aSRafael J. Wysocki */ 195374a2799aSRafael J. Wysocki int pm_genpd_name_detach_cpuidle(const char *name) 195474a2799aSRafael J. Wysocki { 195574a2799aSRafael J. Wysocki return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name)); 195674a2799aSRafael J. Wysocki } 195774a2799aSRafael J. Wysocki 1958d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1959d23b9b00SRafael J. Wysocki 1960d5e4cbfeSRafael J. Wysocki /** 1961ecf00475SRafael J. Wysocki * pm_genpd_default_save_state - Default "save device state" for PM domians. 1962ecf00475SRafael J. Wysocki * @dev: Device to handle. 1963ecf00475SRafael J. Wysocki */ 1964ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1965ecf00475SRafael J. Wysocki { 1966ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1967ecf00475SRafael J. Wysocki 1968ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.save_state; 1969ecf00475SRafael J. Wysocki if (cb) 1970ecf00475SRafael J. Wysocki return cb(dev); 1971ecf00475SRafael J. Wysocki 19720b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 19730b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 19740b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 19750b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 19760b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 19770b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 19780b589741SRafael J. Wysocki else 19790b589741SRafael J. Wysocki cb = NULL; 1980ecf00475SRafael J. Wysocki 19810b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 19820b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 19830b589741SRafael J. Wysocki 19840b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1985ecf00475SRafael J. Wysocki } 1986ecf00475SRafael J. Wysocki 1987ecf00475SRafael J. Wysocki /** 1988ecf00475SRafael J. Wysocki * pm_genpd_default_restore_state - Default PM domians "restore device state". 1989ecf00475SRafael J. Wysocki * @dev: Device to handle. 1990ecf00475SRafael J. Wysocki */ 1991ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1992ecf00475SRafael J. Wysocki { 1993ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1994ecf00475SRafael J. Wysocki 1995ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.restore_state; 1996ecf00475SRafael J. Wysocki if (cb) 1997ecf00475SRafael J. Wysocki return cb(dev); 1998ecf00475SRafael J. Wysocki 19990b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 20000b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 20010b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 20020b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 20030b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 20040b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 20050b589741SRafael J. Wysocki else 20060b589741SRafael J. Wysocki cb = NULL; 2007ecf00475SRafael J. Wysocki 20080b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 20090b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 20100b589741SRafael J. Wysocki 20110b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 2012ecf00475SRafael J. Wysocki } 2013ecf00475SRafael J. Wysocki 20140f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 20150f1d6986SRafael J. Wysocki 2016ecf00475SRafael J. Wysocki /** 2017d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend - Default "device suspend" for PM domians. 2018d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2019d23b9b00SRafael J. Wysocki */ 2020d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev) 2021d23b9b00SRafael J. Wysocki { 2022c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend; 2023d23b9b00SRafael J. Wysocki 2024d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend(dev); 2025d23b9b00SRafael J. Wysocki } 2026d23b9b00SRafael J. Wysocki 2027d23b9b00SRafael J. Wysocki /** 2028d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. 2029d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2030d23b9b00SRafael J. Wysocki */ 2031d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev) 2032d23b9b00SRafael J. Wysocki { 2033c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; 2034d23b9b00SRafael J. Wysocki 20350496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend_late(dev); 2036d23b9b00SRafael J. Wysocki } 2037d23b9b00SRafael J. Wysocki 2038d23b9b00SRafael J. Wysocki /** 2039d23b9b00SRafael J. Wysocki * pm_genpd_default_resume_early - Default "early device resume" for PM domians. 2040d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2041d23b9b00SRafael J. Wysocki */ 2042d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev) 2043d23b9b00SRafael J. Wysocki { 2044c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; 2045d23b9b00SRafael J. Wysocki 20460496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume_early(dev); 2047d23b9b00SRafael J. Wysocki } 2048d23b9b00SRafael J. Wysocki 2049d23b9b00SRafael J. Wysocki /** 2050d23b9b00SRafael J. Wysocki * pm_genpd_default_resume - Default "device resume" for PM domians. 2051d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2052d23b9b00SRafael J. Wysocki */ 2053d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev) 2054d23b9b00SRafael J. Wysocki { 2055c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume; 2056d23b9b00SRafael J. Wysocki 2057d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume(dev); 2058d23b9b00SRafael J. Wysocki } 2059d23b9b00SRafael J. Wysocki 2060d23b9b00SRafael J. Wysocki /** 2061d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze - Default "device freeze" for PM domians. 2062d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2063d23b9b00SRafael J. Wysocki */ 2064d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev) 2065d23b9b00SRafael J. Wysocki { 2066d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; 2067d23b9b00SRafael J. Wysocki 2068d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze(dev); 2069d23b9b00SRafael J. Wysocki } 2070d23b9b00SRafael J. Wysocki 2071d23b9b00SRafael J. Wysocki /** 2072d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. 2073d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2074d23b9b00SRafael J. Wysocki */ 2075d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev) 2076d23b9b00SRafael J. Wysocki { 2077d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; 2078d23b9b00SRafael J. Wysocki 20790496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze_late(dev); 2080d23b9b00SRafael J. Wysocki } 2081d23b9b00SRafael J. Wysocki 2082d23b9b00SRafael J. Wysocki /** 2083d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. 2084d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2085d23b9b00SRafael J. Wysocki */ 2086d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev) 2087d23b9b00SRafael J. Wysocki { 2088d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; 2089d23b9b00SRafael J. Wysocki 20900496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw_early(dev); 2091d23b9b00SRafael J. Wysocki } 2092d23b9b00SRafael J. Wysocki 2093d23b9b00SRafael J. Wysocki /** 2094d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw - Default "device thaw" for PM domians. 2095d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2096d23b9b00SRafael J. Wysocki */ 2097d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev) 2098d23b9b00SRafael J. Wysocki { 2099d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; 2100d23b9b00SRafael J. Wysocki 2101d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw(dev); 2102d23b9b00SRafael J. Wysocki } 2103d23b9b00SRafael J. Wysocki 21040f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 21050f1d6986SRafael J. Wysocki 21060f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend NULL 21070f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late NULL 21080f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early NULL 21090f1d6986SRafael J. Wysocki #define pm_genpd_default_resume NULL 21100f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze NULL 21110f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late NULL 21120f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early NULL 21130f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw NULL 21140f1d6986SRafael J. Wysocki 21150f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */ 21160f1d6986SRafael J. Wysocki 2117d23b9b00SRafael J. Wysocki /** 2118f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 2119f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 2120f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 2121f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 2122f721889fSRafael J. Wysocki */ 2123f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 2124f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 2125f721889fSRafael J. Wysocki { 2126f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 2127f721889fSRafael J. Wysocki return; 2128f721889fSRafael J. Wysocki 21295063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 21305063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 2131f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 2132f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 2133f721889fSRafael J. Wysocki genpd->gov = gov; 2134f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 2135f721889fSRafael J. Wysocki genpd->in_progress = 0; 2136c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 213717b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 213817b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 2139c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 2140c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 2141596ba34bSRafael J. Wysocki genpd->device_count = 0; 2142221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 21436ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2144f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 2145f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 2146f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 2147596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 2148596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 21490496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 2150596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 2151596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 21520496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 2153596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 2154596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 21550496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 2156596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 2157596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 21580496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 2159596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 2160d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 21610496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 2162d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 2163596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 21640496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 2165d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 2166596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 2167ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 2168ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 2169c9914854SRafael J. Wysocki genpd->dev_ops.suspend = pm_genpd_default_suspend; 2170c9914854SRafael J. Wysocki genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late; 2171c9914854SRafael J. Wysocki genpd->dev_ops.resume_early = pm_genpd_default_resume_early; 2172c9914854SRafael J. Wysocki genpd->dev_ops.resume = pm_genpd_default_resume; 2173d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze = pm_genpd_default_freeze; 2174d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; 2175d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; 2176d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw = pm_genpd_default_thaw; 21775125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 21785125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 21795125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 21805125bbf3SRafael J. Wysocki } 2181