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 if (!work_pending(&genpd->power_off_work)) 43756375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 43856375fd4SRafael J. Wysocki } 43956375fd4SRafael J. Wysocki 44056375fd4SRafael J. Wysocki /** 441f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 442f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 443f721889fSRafael J. Wysocki * 444f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 445f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 446f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 447f721889fSRafael J. Wysocki */ 448f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 44917b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 450f721889fSRafael J. Wysocki { 4514605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 4525063ce15SRafael J. Wysocki struct gpd_link *link; 453f721889fSRafael J. Wysocki unsigned int not_suspended; 454c6d22b37SRafael J. Wysocki int ret = 0; 455f721889fSRafael J. Wysocki 456c6d22b37SRafael J. Wysocki start: 457c6d22b37SRafael J. Wysocki /* 458c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 459c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 4605063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 461c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 4623f241775SRafael J. Wysocki * (4) System suspend is in progress. 463c6d22b37SRafael J. Wysocki */ 4643f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 46517877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 4663f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 467f721889fSRafael J. Wysocki return 0; 468f721889fSRafael J. Wysocki 469c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 470f721889fSRafael J. Wysocki return -EBUSY; 471f721889fSRafael J. Wysocki 472f721889fSRafael J. Wysocki not_suspended = 0; 4734605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 4740aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 475feb70af0SRafael J. Wysocki || pdd->dev->power.irq_safe)) 476f721889fSRafael J. Wysocki not_suspended++; 477f721889fSRafael J. Wysocki 478f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 479f721889fSRafael J. Wysocki return -EBUSY; 480f721889fSRafael J. Wysocki 481c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 482c6d22b37SRafael J. Wysocki /* 483c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 484c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 485c6d22b37SRafael J. Wysocki */ 486c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 487c6d22b37SRafael J. Wysocki return 0; 488c6d22b37SRafael J. Wysocki } 489c6d22b37SRafael J. Wysocki 490f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 491f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 492f721889fSRafael J. Wysocki return -EAGAIN; 493f721889fSRafael J. Wysocki } 494f721889fSRafael J. Wysocki 49517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 496c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 49717b75ecaSRafael J. Wysocki 4984605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 4993c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 5004605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 5013f241775SRafael J. Wysocki 5023f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 5033f241775SRafael J. Wysocki goto out; 5043f241775SRafael J. Wysocki 505697a7f37SRafael J. Wysocki if (ret) { 506697a7f37SRafael J. Wysocki genpd_set_active(genpd); 507697a7f37SRafael J. Wysocki goto out; 508697a7f37SRafael J. Wysocki } 509f721889fSRafael J. Wysocki 510c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 511c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 512c6d22b37SRafael J. Wysocki goto start; 513c6d22b37SRafael J. Wysocki } 514c6d22b37SRafael J. Wysocki } 51517b75ecaSRafael J. Wysocki 516cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 517cbc9ef02SRafael J. Wysocki /* 518cbc9ef02SRafael J. Wysocki * If cpu_data is set, cpuidle should turn the domain off when 519cbc9ef02SRafael J. Wysocki * the CPU in it is idle. In that case we don't decrement the 520cbc9ef02SRafael J. Wysocki * subdomain counts of the master domains, so that power is not 521cbc9ef02SRafael J. Wysocki * removed from the current domain prematurely as a result of 522cbc9ef02SRafael J. Wysocki * cutting off the masters' power. 523cbc9ef02SRafael J. Wysocki */ 524cbc9ef02SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 525cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 526cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = false; 527cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 528cbc9ef02SRafael J. Wysocki goto out; 529cbc9ef02SRafael J. Wysocki } 530cbc9ef02SRafael J. Wysocki 5313c07cbc4SRafael J. Wysocki if (genpd->power_off) { 5320140d8bdSRafael J. Wysocki ktime_t time_start; 5330140d8bdSRafael J. Wysocki s64 elapsed_ns; 5340140d8bdSRafael J. Wysocki 5353c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 5363c07cbc4SRafael J. Wysocki ret = -EBUSY; 537c6d22b37SRafael J. Wysocki goto out; 538c6d22b37SRafael J. Wysocki } 53917b75ecaSRafael J. Wysocki 5400140d8bdSRafael J. Wysocki time_start = ktime_get(); 5410140d8bdSRafael J. Wysocki 5423c07cbc4SRafael J. Wysocki /* 5435063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 5445063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 5453c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 5463c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 5473c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 5483c07cbc4SRafael J. Wysocki * happen very often). 5493c07cbc4SRafael J. Wysocki */ 550d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 551d2805402SRafael J. Wysocki if (ret == -EBUSY) { 552d2805402SRafael J. Wysocki genpd_set_active(genpd); 553d2805402SRafael J. Wysocki goto out; 554d2805402SRafael J. Wysocki } 5550140d8bdSRafael J. Wysocki 5560140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 557e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_off_latency_ns) { 5580140d8bdSRafael J. Wysocki genpd->power_off_latency_ns = elapsed_ns; 5596ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 560e84b2c20SRafael J. Wysocki if (genpd->name) 561e84b2c20SRafael J. Wysocki pr_warning("%s: Power-off latency exceeded, " 562e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 563e84b2c20SRafael J. Wysocki elapsed_ns); 564e84b2c20SRafael J. Wysocki } 565d2805402SRafael J. Wysocki } 566f721889fSRafael J. Wysocki 56717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 568221e9b58SRafael J. Wysocki 5695063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5705063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 5715063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 5725063ce15SRafael J. Wysocki } 57317b75ecaSRafael J. Wysocki 574c6d22b37SRafael J. Wysocki out: 575c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 576c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 577c6d22b37SRafael J. Wysocki return ret; 578f721889fSRafael J. Wysocki } 579f721889fSRafael J. Wysocki 580f721889fSRafael J. Wysocki /** 581f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 582f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 583f721889fSRafael J. Wysocki */ 584f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 585f721889fSRafael J. Wysocki { 586f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 587f721889fSRafael J. Wysocki 588f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 589f721889fSRafael J. Wysocki 59017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 591f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 59217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 593f721889fSRafael J. Wysocki } 594f721889fSRafael J. Wysocki 595f721889fSRafael J. Wysocki /** 596f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 597f721889fSRafael J. Wysocki * @dev: Device to suspend. 598f721889fSRafael J. Wysocki * 599f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 600f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 601f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 602f721889fSRafael J. Wysocki */ 603f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 604f721889fSRafael J. Wysocki { 605f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 606b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 607d5e4cbfeSRafael J. Wysocki int ret; 608f721889fSRafael J. Wysocki 609f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 610f721889fSRafael J. Wysocki 6115248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6125248051bSRafael J. Wysocki if (IS_ERR(genpd)) 613f721889fSRafael J. Wysocki return -EINVAL; 614f721889fSRafael J. Wysocki 6150aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 6160aa2a221SRafael J. Wysocki 617b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 618b02c999aSRafael J. Wysocki if (stop_ok && !stop_ok(dev)) 619b02c999aSRafael J. Wysocki return -EBUSY; 620b02c999aSRafael J. Wysocki 621d5e4cbfeSRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 622f721889fSRafael J. Wysocki if (ret) 62317b75ecaSRafael J. Wysocki return ret; 62417b75ecaSRafael J. Wysocki 6250aa2a221SRafael J. Wysocki /* 6260aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 6270aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 6280aa2a221SRafael J. Wysocki */ 6290aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 6300aa2a221SRafael J. Wysocki return 0; 6310aa2a221SRafael J. Wysocki 632c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 633f721889fSRafael J. Wysocki genpd->in_progress++; 634f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 635f721889fSRafael J. Wysocki genpd->in_progress--; 636c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 637f721889fSRafael J. Wysocki 638f721889fSRafael J. Wysocki return 0; 639f721889fSRafael J. Wysocki } 640f721889fSRafael J. Wysocki 641f721889fSRafael J. Wysocki /** 642f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 643f721889fSRafael J. Wysocki * @dev: Device to resume. 644f721889fSRafael J. Wysocki * 645f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 646f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 647f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 648f721889fSRafael J. Wysocki */ 649f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 650f721889fSRafael J. Wysocki { 651f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 652c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 653f721889fSRafael J. Wysocki int ret; 654f721889fSRafael J. Wysocki 655f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 656f721889fSRafael J. Wysocki 6575248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6585248051bSRafael J. Wysocki if (IS_ERR(genpd)) 659f721889fSRafael J. Wysocki return -EINVAL; 660f721889fSRafael J. Wysocki 6610aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 6620aa2a221SRafael J. Wysocki 6630aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 6640aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 665e2e3e4e5SRafael J. Wysocki return genpd_start_dev_no_timing(genpd, dev); 6660aa2a221SRafael J. Wysocki 667c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 6683f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 6693f241775SRafael J. Wysocki if (ret) { 6703f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 6713f241775SRafael J. Wysocki return ret; 6723f241775SRafael J. Wysocki } 67317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 674c6d22b37SRafael J. Wysocki genpd->resume_count++; 675c6d22b37SRafael J. Wysocki for (;;) { 676c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 677c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 678c6d22b37SRafael J. Wysocki /* 679c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 680c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 681c6d22b37SRafael J. Wysocki * not wait. 682c6d22b37SRafael J. Wysocki */ 683c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 684c6d22b37SRafael J. Wysocki break; 685c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 686c6d22b37SRafael J. Wysocki 687c6d22b37SRafael J. Wysocki schedule(); 688c6d22b37SRafael J. Wysocki 689c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 690c6d22b37SRafael J. Wysocki } 691c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 692cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 693c6d22b37SRafael J. Wysocki genpd->resume_count--; 694c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 69517b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 696c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 69717b75ecaSRafael J. Wysocki 698f721889fSRafael J. Wysocki return 0; 699f721889fSRafael J. Wysocki } 700f721889fSRafael J. Wysocki 70117f2ae7fSRafael J. Wysocki /** 70217f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 70317f2ae7fSRafael J. Wysocki */ 70417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 70517f2ae7fSRafael J. Wysocki { 70617f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 70717f2ae7fSRafael J. Wysocki 70817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 70917f2ae7fSRafael J. Wysocki 71017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 71117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 71217f2ae7fSRafael J. Wysocki 71317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 71417f2ae7fSRafael J. Wysocki } 71517f2ae7fSRafael J. Wysocki 716f721889fSRafael J. Wysocki #else 717f721889fSRafael J. Wysocki 7186ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 7196ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 7206ff7bb0dSRafael J. Wysocki { 7216ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 7226ff7bb0dSRafael J. Wysocki } 7236ff7bb0dSRafael J. Wysocki 724f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 725f721889fSRafael J. Wysocki 726f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 727f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 728f721889fSRafael J. Wysocki 729f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 730f721889fSRafael J. Wysocki 731596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 732596ba34bSRafael J. Wysocki 73377f827deSRafael J. Wysocki /** 73477f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 73577f827deSRafael J. Wysocki * @genpd: PM domain to check. 73677f827deSRafael J. Wysocki */ 73777f827deSRafael J. Wysocki static bool pm_genpd_present(struct generic_pm_domain *genpd) 73877f827deSRafael J. Wysocki { 73977f827deSRafael J. Wysocki struct generic_pm_domain *gpd; 74077f827deSRafael J. Wysocki 74177f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 74277f827deSRafael J. Wysocki return false; 74377f827deSRafael J. Wysocki 74477f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 74577f827deSRafael J. Wysocki if (gpd == genpd) 74677f827deSRafael J. Wysocki return true; 74777f827deSRafael J. Wysocki 74877f827deSRafael J. Wysocki return false; 74977f827deSRafael J. Wysocki } 75077f827deSRafael J. Wysocki 751d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 752d5e4cbfeSRafael J. Wysocki struct device *dev) 753d5e4cbfeSRafael J. Wysocki { 754d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 755d5e4cbfeSRafael J. Wysocki } 756d5e4cbfeSRafael J. Wysocki 757d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) 758d23b9b00SRafael J. Wysocki { 759d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); 760d23b9b00SRafael J. Wysocki } 761d23b9b00SRafael J. Wysocki 762d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) 763d23b9b00SRafael J. Wysocki { 764d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); 765d23b9b00SRafael J. Wysocki } 766d23b9b00SRafael J. Wysocki 767d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) 768d23b9b00SRafael J. Wysocki { 769d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); 770d23b9b00SRafael J. Wysocki } 771d23b9b00SRafael J. Wysocki 772d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) 773d23b9b00SRafael J. Wysocki { 774d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume, dev); 775d23b9b00SRafael J. Wysocki } 776d23b9b00SRafael J. Wysocki 777d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) 778d23b9b00SRafael J. Wysocki { 779d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); 780d23b9b00SRafael J. Wysocki } 781d23b9b00SRafael J. Wysocki 782d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) 783d23b9b00SRafael J. Wysocki { 784d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); 785d23b9b00SRafael J. Wysocki } 786d23b9b00SRafael J. Wysocki 787d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) 788d23b9b00SRafael J. Wysocki { 789d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); 790d23b9b00SRafael J. Wysocki } 791d23b9b00SRafael J. Wysocki 792d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) 793d23b9b00SRafael J. Wysocki { 794d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); 795d23b9b00SRafael J. Wysocki } 796d23b9b00SRafael J. Wysocki 797596ba34bSRafael J. Wysocki /** 7985063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 799596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 800596ba34bSRafael J. Wysocki * 801596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 8025063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 803596ba34bSRafael J. Wysocki * 80477f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 80577f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 80677f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 80777f827deSRafael J. Wysocki * parallel). 808596ba34bSRafael J. Wysocki */ 809596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 810596ba34bSRafael J. Wysocki { 8115063ce15SRafael J. Wysocki struct gpd_link *link; 812596ba34bSRafael J. Wysocki 81317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 814596ba34bSRafael J. Wysocki return; 815596ba34bSRafael J. Wysocki 816c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 817c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 818596ba34bSRafael J. Wysocki return; 819596ba34bSRafael J. Wysocki 820596ba34bSRafael J. Wysocki if (genpd->power_off) 821596ba34bSRafael J. Wysocki genpd->power_off(genpd); 822596ba34bSRafael J. Wysocki 82317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 8245063ce15SRafael J. Wysocki 8255063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 8265063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 8275063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 828596ba34bSRafael J. Wysocki } 829596ba34bSRafael J. Wysocki } 830596ba34bSRafael J. Wysocki 831596ba34bSRafael J. Wysocki /** 832802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 833802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 834802d8b49SRafael J. Wysocki * 83577f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 83677f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 83777f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 83877f827deSRafael J. Wysocki * parallel). 839802d8b49SRafael J. Wysocki */ 840802d8b49SRafael J. Wysocki static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) 841802d8b49SRafael J. Wysocki { 842802d8b49SRafael J. Wysocki struct gpd_link *link; 843802d8b49SRafael J. Wysocki 844802d8b49SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) 845802d8b49SRafael J. Wysocki return; 846802d8b49SRafael J. Wysocki 847802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 848802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(link->master); 849802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 850802d8b49SRafael J. Wysocki } 851802d8b49SRafael J. Wysocki 852802d8b49SRafael J. Wysocki if (genpd->power_on) 853802d8b49SRafael J. Wysocki genpd->power_on(genpd); 854802d8b49SRafael J. Wysocki 855802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 856802d8b49SRafael J. Wysocki } 857802d8b49SRafael J. Wysocki 858802d8b49SRafael J. Wysocki /** 8594ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 8604ecd6e65SRafael J. Wysocki * @dev: Device to check. 8614ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 8624ecd6e65SRafael J. Wysocki * 8634ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 8644ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 8654ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 8664ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 8674ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 8684ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 8694ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8704ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8714ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8724ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8734ecd6e65SRafael J. Wysocki */ 8744ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 8754ecd6e65SRafael J. Wysocki { 8764ecd6e65SRafael J. Wysocki bool active_wakeup; 8774ecd6e65SRafael J. Wysocki 8784ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8794ecd6e65SRafael J. Wysocki return false; 8804ecd6e65SRafael J. Wysocki 881d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8824ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8834ecd6e65SRafael J. Wysocki } 8844ecd6e65SRafael J. Wysocki 8854ecd6e65SRafael J. Wysocki /** 886596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 887596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 888596ba34bSRafael J. Wysocki * 889596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 890596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 891596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 892596ba34bSRafael J. Wysocki * consisting of I/O devices. 893596ba34bSRafael J. Wysocki */ 894596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 895596ba34bSRafael J. Wysocki { 896596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 897b6c10c84SRafael J. Wysocki int ret; 898596ba34bSRafael J. Wysocki 899596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 900596ba34bSRafael J. Wysocki 901596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 902596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 903596ba34bSRafael J. Wysocki return -EINVAL; 904596ba34bSRafael J. Wysocki 90517b75ecaSRafael J. Wysocki /* 90617b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 90717b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 90817b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 90917b75ecaSRafael J. Wysocki */ 91017b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 91117b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 91217b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 91317b75ecaSRafael J. Wysocki 91417b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 91517b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 91617b75ecaSRafael J. Wysocki return -EBUSY; 91717b75ecaSRafael J. Wysocki } 91817b75ecaSRafael J. Wysocki 9194ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 9204ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 9214ecd6e65SRafael J. Wysocki 92217b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 923596ba34bSRafael J. Wysocki 92465533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 92565533bbfSRafael J. Wysocki genpd->suspended_count = 0; 92617b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 92765533bbfSRafael J. Wysocki } 92817b75ecaSRafael J. Wysocki 92917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 930596ba34bSRafael J. Wysocki 931596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 93217b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 933596ba34bSRafael J. Wysocki return 0; 934596ba34bSRafael J. Wysocki } 935596ba34bSRafael J. Wysocki 936596ba34bSRafael J. Wysocki /* 93717b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 93817b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 939d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 94017b75ecaSRafael J. Wysocki * to make it operational. 941596ba34bSRafael J. Wysocki */ 94217b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 943596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 944596ba34bSRafael J. Wysocki 945b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 946b6c10c84SRafael J. Wysocki if (ret) { 947b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 948b6c10c84SRafael J. Wysocki 949b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 950b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 951b6c10c84SRafael J. Wysocki 952b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 95317b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 954b6c10c84SRafael J. Wysocki } 95517b75ecaSRafael J. Wysocki 95617b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 957b6c10c84SRafael J. Wysocki return ret; 958596ba34bSRafael J. Wysocki } 959596ba34bSRafael J. Wysocki 960596ba34bSRafael J. Wysocki /** 961596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 962596ba34bSRafael J. Wysocki * @dev: Device to suspend. 963596ba34bSRafael J. Wysocki * 964596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 965596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 966596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 967596ba34bSRafael J. Wysocki */ 968596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 969596ba34bSRafael J. Wysocki { 970596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 971596ba34bSRafael J. Wysocki 972596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 973596ba34bSRafael J. Wysocki 974596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 975596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 976596ba34bSRafael J. Wysocki return -EINVAL; 977596ba34bSRafael J. Wysocki 978d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); 979596ba34bSRafael J. Wysocki } 980596ba34bSRafael J. Wysocki 981596ba34bSRafael J. Wysocki /** 9820496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 983596ba34bSRafael J. Wysocki * @dev: Device to suspend. 984596ba34bSRafael J. Wysocki * 985596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 986596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 987596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 988596ba34bSRafael J. Wysocki */ 9890496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 990596ba34bSRafael J. Wysocki { 991596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 992596ba34bSRafael J. Wysocki 993596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 994596ba34bSRafael J. Wysocki 995596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 996596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 997596ba34bSRafael J. Wysocki return -EINVAL; 998596ba34bSRafael J. Wysocki 9990496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); 10000496c8aeSRafael J. Wysocki } 1001596ba34bSRafael J. Wysocki 10020496c8aeSRafael J. Wysocki /** 10030496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 10040496c8aeSRafael J. Wysocki * @dev: Device to suspend. 10050496c8aeSRafael J. Wysocki * 10060496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 10070496c8aeSRafael J. Wysocki * been stopped. 10080496c8aeSRafael J. Wysocki */ 10090496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 10100496c8aeSRafael J. Wysocki { 10110496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1012596ba34bSRafael J. Wysocki 10130496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10140496c8aeSRafael J. Wysocki 10150496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10160496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10170496c8aeSRafael J. Wysocki return -EINVAL; 10180496c8aeSRafael J. Wysocki 1019dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 10200496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 1021d4f2d87aSRafael J. Wysocki return 0; 1022d4f2d87aSRafael J. Wysocki 1023d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 1024596ba34bSRafael J. Wysocki 1025596ba34bSRafael J. Wysocki /* 1026596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1027596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1028596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 1029596ba34bSRafael J. Wysocki */ 1030596ba34bSRafael J. Wysocki genpd->suspended_count++; 1031596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 1032596ba34bSRafael J. Wysocki 1033596ba34bSRafael J. Wysocki return 0; 1034596ba34bSRafael J. Wysocki } 1035596ba34bSRafael J. Wysocki 1036596ba34bSRafael J. Wysocki /** 10370496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 1038596ba34bSRafael J. Wysocki * @dev: Device to resume. 1039596ba34bSRafael J. Wysocki * 10400496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 1041596ba34bSRafael J. Wysocki */ 1042596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 1043596ba34bSRafael J. Wysocki { 1044596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1045596ba34bSRafael J. Wysocki 1046596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1047596ba34bSRafael J. Wysocki 1048596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1049596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1050596ba34bSRafael J. Wysocki return -EINVAL; 1051596ba34bSRafael J. Wysocki 1052dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 1053cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 1054596ba34bSRafael J. Wysocki return 0; 1055596ba34bSRafael J. Wysocki 1056596ba34bSRafael J. Wysocki /* 1057596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1058596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1059596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 1060596ba34bSRafael J. Wysocki */ 1061802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(genpd); 1062596ba34bSRafael J. Wysocki genpd->suspended_count--; 1063596ba34bSRafael J. Wysocki 10640496c8aeSRafael J. Wysocki return genpd_start_dev(genpd, dev); 1065596ba34bSRafael J. Wysocki } 1066596ba34bSRafael J. Wysocki 1067596ba34bSRafael J. Wysocki /** 10680496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 10690496c8aeSRafael J. Wysocki * @dev: Device to resume. 10700496c8aeSRafael J. Wysocki * 10710496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 10720496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10730496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10740496c8aeSRafael J. Wysocki * devices. 10750496c8aeSRafael J. Wysocki */ 10760496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 10770496c8aeSRafael J. Wysocki { 10780496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10790496c8aeSRafael J. Wysocki 10800496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10810496c8aeSRafael J. Wysocki 10820496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10830496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10840496c8aeSRafael J. Wysocki return -EINVAL; 10850496c8aeSRafael J. Wysocki 10860496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); 10870496c8aeSRafael J. Wysocki } 10880496c8aeSRafael J. Wysocki 10890496c8aeSRafael J. Wysocki /** 10900496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 1091596ba34bSRafael J. Wysocki * @dev: Device to resume. 1092596ba34bSRafael J. Wysocki * 1093596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 1094596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1095596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1096596ba34bSRafael J. Wysocki */ 1097596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 1098596ba34bSRafael J. Wysocki { 1099596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1100596ba34bSRafael J. Wysocki 1101596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1102596ba34bSRafael J. Wysocki 1103596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1104596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1105596ba34bSRafael J. Wysocki return -EINVAL; 1106596ba34bSRafael J. Wysocki 1107d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); 1108596ba34bSRafael J. Wysocki } 1109596ba34bSRafael J. Wysocki 1110596ba34bSRafael J. Wysocki /** 11110496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 1112596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1113596ba34bSRafael J. Wysocki * 1114596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 1115596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1116596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1117596ba34bSRafael J. Wysocki */ 1118596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 1119596ba34bSRafael J. Wysocki { 1120596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1121596ba34bSRafael J. Wysocki 1122596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1123596ba34bSRafael J. Wysocki 1124596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1125596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1126596ba34bSRafael J. Wysocki return -EINVAL; 1127596ba34bSRafael J. Wysocki 1128d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); 1129596ba34bSRafael J. Wysocki } 1130596ba34bSRafael J. Wysocki 1131596ba34bSRafael J. Wysocki /** 11320496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 11330496c8aeSRafael J. Wysocki * @dev: Device to freeze. 11340496c8aeSRafael J. Wysocki * 11350496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 11360496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 11370496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 11380496c8aeSRafael J. Wysocki * devices. 11390496c8aeSRafael J. Wysocki */ 11400496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 11410496c8aeSRafael J. Wysocki { 11420496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 11430496c8aeSRafael J. Wysocki 11440496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 11450496c8aeSRafael J. Wysocki 11460496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11470496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11480496c8aeSRafael J. Wysocki return -EINVAL; 11490496c8aeSRafael J. Wysocki 11500496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); 11510496c8aeSRafael J. Wysocki } 11520496c8aeSRafael J. Wysocki 11530496c8aeSRafael J. Wysocki /** 11540496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1155596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1156596ba34bSRafael J. Wysocki * 1157596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1158596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1159596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1160596ba34bSRafael J. Wysocki * devices. 1161596ba34bSRafael J. Wysocki */ 1162596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1163596ba34bSRafael J. Wysocki { 1164596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1165596ba34bSRafael J. Wysocki 1166596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1167596ba34bSRafael J. Wysocki 1168596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1169596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1170596ba34bSRafael J. Wysocki return -EINVAL; 1171596ba34bSRafael J. Wysocki 1172dbf37414SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); 1173596ba34bSRafael J. Wysocki } 1174596ba34bSRafael J. Wysocki 1175596ba34bSRafael J. Wysocki /** 11760496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1177596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1178596ba34bSRafael J. Wysocki * 11790496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 11800496c8aeSRafael J. Wysocki * before the system transition. 1181596ba34bSRafael J. Wysocki */ 1182596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1183596ba34bSRafael J. Wysocki { 1184596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1185596ba34bSRafael J. Wysocki 1186596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1187596ba34bSRafael J. Wysocki 1188596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1189596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1190596ba34bSRafael J. Wysocki return -EINVAL; 1191596ba34bSRafael J. Wysocki 1192dbf37414SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); 11930496c8aeSRafael J. Wysocki } 1194596ba34bSRafael J. Wysocki 11950496c8aeSRafael J. Wysocki /** 11960496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 11970496c8aeSRafael J. Wysocki * @dev: Device to thaw. 11980496c8aeSRafael J. Wysocki * 11990496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 12000496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 12010496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 12020496c8aeSRafael J. Wysocki * devices. 12030496c8aeSRafael J. Wysocki */ 12040496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 12050496c8aeSRafael J. Wysocki { 12060496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1207596ba34bSRafael J. Wysocki 12080496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 12090496c8aeSRafael J. Wysocki 12100496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 12110496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 12120496c8aeSRafael J. Wysocki return -EINVAL; 12130496c8aeSRafael J. Wysocki 12140496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); 1215596ba34bSRafael J. Wysocki } 1216596ba34bSRafael J. Wysocki 1217596ba34bSRafael J. Wysocki /** 1218596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1219596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1220596ba34bSRafael J. Wysocki * 1221596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1222596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1223596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1224596ba34bSRafael J. Wysocki */ 1225596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1226596ba34bSRafael J. Wysocki { 1227596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1228596ba34bSRafael J. Wysocki 1229596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1230596ba34bSRafael J. Wysocki 1231596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1232596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1233596ba34bSRafael J. Wysocki return -EINVAL; 1234596ba34bSRafael J. Wysocki 1235d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); 1236596ba34bSRafael J. Wysocki } 1237596ba34bSRafael J. Wysocki 1238596ba34bSRafael J. Wysocki /** 12390496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1240596ba34bSRafael J. Wysocki * @dev: Device to resume. 1241596ba34bSRafael J. Wysocki * 12420496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 12430496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1244596ba34bSRafael J. Wysocki */ 1245596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1246596ba34bSRafael J. Wysocki { 1247596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1248596ba34bSRafael J. Wysocki 1249596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1250596ba34bSRafael J. Wysocki 1251596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1252596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1253596ba34bSRafael J. Wysocki return -EINVAL; 1254596ba34bSRafael J. Wysocki 1255596ba34bSRafael J. Wysocki /* 1256596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1257596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1258596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 125965533bbfSRafael J. Wysocki * 126065533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 126165533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 126265533bbfSRafael J. Wysocki */ 126365533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 126465533bbfSRafael J. Wysocki /* 126565533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1266802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 1267802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1268596ba34bSRafael J. Wysocki */ 126917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1270596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1271596ba34bSRafael J. Wysocki /* 127265533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 127365533bbfSRafael J. Wysocki * sure it will be off going forward. 1274596ba34bSRafael J. Wysocki */ 1275596ba34bSRafael J. Wysocki if (genpd->power_off) 1276596ba34bSRafael J. Wysocki genpd->power_off(genpd); 127765533bbfSRafael J. Wysocki 1278596ba34bSRafael J. Wysocki return 0; 1279596ba34bSRafael J. Wysocki } 128065533bbfSRafael J. Wysocki } 1281596ba34bSRafael J. Wysocki 128218dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 128318dd2eceSRafael J. Wysocki return 0; 128418dd2eceSRafael J. Wysocki 1285802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(genpd); 1286596ba34bSRafael J. Wysocki 1287dbf37414SRafael J. Wysocki return genpd_start_dev(genpd, dev); 1288596ba34bSRafael J. Wysocki } 1289596ba34bSRafael J. Wysocki 1290596ba34bSRafael J. Wysocki /** 1291596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1292596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1293596ba34bSRafael J. Wysocki * 1294596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1295596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1296596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1297596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1298596ba34bSRafael J. Wysocki */ 1299596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1300596ba34bSRafael J. Wysocki { 1301596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1302596ba34bSRafael J. Wysocki bool run_complete; 1303596ba34bSRafael J. Wysocki 1304596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1305596ba34bSRafael J. Wysocki 1306596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1307596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1308596ba34bSRafael J. Wysocki return; 1309596ba34bSRafael J. Wysocki 1310596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1311596ba34bSRafael J. Wysocki 1312596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1313596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1314596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1315596ba34bSRafael J. Wysocki 1316596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1317596ba34bSRafael J. Wysocki 1318596ba34bSRafael J. Wysocki if (run_complete) { 1319596ba34bSRafael J. Wysocki pm_generic_complete(dev); 13206f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1321596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 13226f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 1323596ba34bSRafael J. Wysocki } 1324596ba34bSRafael J. Wysocki } 1325596ba34bSRafael J. Wysocki 132677f827deSRafael J. Wysocki /** 132777f827deSRafael J. Wysocki * pm_genpd_syscore_switch - Switch power during system core suspend or resume. 132877f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 132977f827deSRafael J. Wysocki * 133077f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 133177f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 133277f827deSRafael J. Wysocki */ 133377f827deSRafael J. Wysocki void pm_genpd_syscore_switch(struct device *dev, bool suspend) 133477f827deSRafael J. Wysocki { 133577f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 133677f827deSRafael J. Wysocki 133777f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 133877f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 133977f827deSRafael J. Wysocki return; 134077f827deSRafael J. Wysocki 134177f827deSRafael J. Wysocki if (suspend) { 134277f827deSRafael J. Wysocki genpd->suspended_count++; 134377f827deSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 134477f827deSRafael J. Wysocki } else { 134577f827deSRafael J. Wysocki pm_genpd_sync_poweron(genpd); 134677f827deSRafael J. Wysocki genpd->suspended_count--; 134777f827deSRafael J. Wysocki } 134877f827deSRafael J. Wysocki } 134977f827deSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch); 135077f827deSRafael J. Wysocki 1351596ba34bSRafael J. Wysocki #else 1352596ba34bSRafael J. Wysocki 1353596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1354596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 13550496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1356596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 13570496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1358596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1359596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1360596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 13610496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1362596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 13630496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1364596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1365596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1366596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1367596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1368596ba34bSRafael J. Wysocki 1369596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1370596ba34bSRafael J. Wysocki 13711d5fcfecSRafael J. Wysocki static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) 13721d5fcfecSRafael J. Wysocki { 13731d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13741d5fcfecSRafael J. Wysocki 13751d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 13761d5fcfecSRafael J. Wysocki if (!gpd_data) 13771d5fcfecSRafael J. Wysocki return NULL; 13781d5fcfecSRafael J. Wysocki 13791d5fcfecSRafael J. Wysocki mutex_init(&gpd_data->lock); 13801d5fcfecSRafael J. Wysocki gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 13811d5fcfecSRafael J. Wysocki dev_pm_qos_add_notifier(dev, &gpd_data->nb); 13821d5fcfecSRafael J. Wysocki return gpd_data; 13831d5fcfecSRafael J. Wysocki } 13841d5fcfecSRafael J. Wysocki 13851d5fcfecSRafael J. Wysocki static void __pm_genpd_free_dev_data(struct device *dev, 13861d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 13871d5fcfecSRafael J. Wysocki { 13881d5fcfecSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13891d5fcfecSRafael J. Wysocki kfree(gpd_data); 13901d5fcfecSRafael J. Wysocki } 13911d5fcfecSRafael J. Wysocki 1392f721889fSRafael J. Wysocki /** 1393b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1394f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1395f721889fSRafael J. Wysocki * @dev: Device to be added. 1396b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1397f721889fSRafael J. Wysocki */ 1398b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1399b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1400f721889fSRafael J. Wysocki { 14011d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; 14024605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1403f721889fSRafael J. Wysocki int ret = 0; 1404f721889fSRafael J. Wysocki 1405f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1406f721889fSRafael J. Wysocki 1407f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1408f721889fSRafael J. Wysocki return -EINVAL; 1409f721889fSRafael J. Wysocki 14101d5fcfecSRafael J. Wysocki gpd_data_new = __pm_genpd_alloc_dev_data(dev); 14111d5fcfecSRafael J. Wysocki if (!gpd_data_new) 14126ff7bb0dSRafael J. Wysocki return -ENOMEM; 14136ff7bb0dSRafael J. Wysocki 141417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1415f721889fSRafael J. Wysocki 1416596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1417596ba34bSRafael J. Wysocki ret = -EAGAIN; 1418596ba34bSRafael J. Wysocki goto out; 1419596ba34bSRafael J. Wysocki } 1420596ba34bSRafael J. Wysocki 14214605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 14224605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1423f721889fSRafael J. Wysocki ret = -EINVAL; 1424f721889fSRafael J. Wysocki goto out; 1425f721889fSRafael J. Wysocki } 1426f721889fSRafael J. Wysocki 14271d5fcfecSRafael J. Wysocki ret = dev_pm_get_subsys_data(dev); 14281d5fcfecSRafael J. Wysocki if (ret) 14291d5fcfecSRafael J. Wysocki goto out; 14301d5fcfecSRafael J. Wysocki 1431596ba34bSRafael J. Wysocki genpd->device_count++; 14326ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1433f721889fSRafael J. Wysocki 14346ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 14351d5fcfecSRafael J. Wysocki 14366ff7bb0dSRafael J. Wysocki dev->pm_domain = &genpd->domain; 14371d5fcfecSRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 14381d5fcfecSRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 14391d5fcfecSRafael J. Wysocki } else { 14401d5fcfecSRafael J. Wysocki gpd_data = gpd_data_new; 1441cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 14421d5fcfecSRafael J. Wysocki } 14431d5fcfecSRafael J. Wysocki gpd_data->refcount++; 1444b02c999aSRafael J. Wysocki if (td) 1445b02c999aSRafael J. Wysocki gpd_data->td = *td; 1446f721889fSRafael J. Wysocki 14471d5fcfecSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 14481d5fcfecSRafael J. Wysocki 14491d5fcfecSRafael J. Wysocki mutex_lock(&gpd_data->lock); 14501d5fcfecSRafael J. Wysocki gpd_data->base.dev = dev; 14511d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 14521d5fcfecSRafael J. Wysocki gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 14536ff7bb0dSRafael J. Wysocki gpd_data->td.constraint_changed = true; 14546ff7bb0dSRafael J. Wysocki gpd_data->td.effective_constraint_ns = -1; 14556ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 14566ff7bb0dSRafael J. Wysocki 1457f721889fSRafael J. Wysocki out: 145817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1459f721889fSRafael J. Wysocki 14601d5fcfecSRafael J. Wysocki if (gpd_data != gpd_data_new) 14611d5fcfecSRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data_new); 14621d5fcfecSRafael J. Wysocki 1463f721889fSRafael J. Wysocki return ret; 1464f721889fSRafael J. Wysocki } 1465f721889fSRafael J. Wysocki 1466f721889fSRafael J. Wysocki /** 1467c8aa130bSThomas Abraham * __pm_genpd_of_add_device - Add a device to an I/O PM domain. 1468c8aa130bSThomas Abraham * @genpd_node: Device tree node pointer representing a PM domain to which the 1469c8aa130bSThomas Abraham * the device is added to. 1470c8aa130bSThomas Abraham * @dev: Device to be added. 1471c8aa130bSThomas Abraham * @td: Set of PM QoS timing parameters to attach to the device. 1472c8aa130bSThomas Abraham */ 1473c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, 1474c8aa130bSThomas Abraham struct gpd_timing_data *td) 1475c8aa130bSThomas Abraham { 1476c8aa130bSThomas Abraham struct generic_pm_domain *genpd = NULL, *gpd; 1477c8aa130bSThomas Abraham 1478c8aa130bSThomas Abraham dev_dbg(dev, "%s()\n", __func__); 1479c8aa130bSThomas Abraham 1480c8aa130bSThomas Abraham if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) 1481c8aa130bSThomas Abraham return -EINVAL; 1482c8aa130bSThomas Abraham 1483c8aa130bSThomas Abraham mutex_lock(&gpd_list_lock); 1484c8aa130bSThomas Abraham list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1485c8aa130bSThomas Abraham if (gpd->of_node == genpd_node) { 1486c8aa130bSThomas Abraham genpd = gpd; 1487c8aa130bSThomas Abraham break; 1488c8aa130bSThomas Abraham } 1489c8aa130bSThomas Abraham } 1490c8aa130bSThomas Abraham mutex_unlock(&gpd_list_lock); 1491c8aa130bSThomas Abraham 1492c8aa130bSThomas Abraham if (!genpd) 1493c8aa130bSThomas Abraham return -EINVAL; 1494c8aa130bSThomas Abraham 1495c8aa130bSThomas Abraham return __pm_genpd_add_device(genpd, dev, td); 1496c8aa130bSThomas Abraham } 1497c8aa130bSThomas Abraham 1498b5abb085SRafael J. Wysocki 1499b5abb085SRafael J. Wysocki /** 1500b5abb085SRafael J. Wysocki * __pm_genpd_name_add_device - Find I/O PM domain and add a device to it. 1501b5abb085SRafael J. Wysocki * @domain_name: Name of the PM domain to add the device to. 1502b5abb085SRafael J. Wysocki * @dev: Device to be added. 1503b5abb085SRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1504b5abb085SRafael J. Wysocki */ 1505b5abb085SRafael J. Wysocki int __pm_genpd_name_add_device(const char *domain_name, struct device *dev, 1506b5abb085SRafael J. Wysocki struct gpd_timing_data *td) 1507b5abb085SRafael J. Wysocki { 15088bc0251dSRafael J. Wysocki return __pm_genpd_add_device(pm_genpd_lookup_name(domain_name), dev, td); 1509b5abb085SRafael J. Wysocki } 1510b5abb085SRafael J. Wysocki 1511c8aa130bSThomas Abraham /** 1512f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1513f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1514f721889fSRafael J. Wysocki * @dev: Device to be removed. 1515f721889fSRafael J. Wysocki */ 1516f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1517f721889fSRafael J. Wysocki struct device *dev) 1518f721889fSRafael J. Wysocki { 15196ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 15204605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 15211d5fcfecSRafael J. Wysocki bool remove = false; 1522efa69025SRafael J. Wysocki int ret = 0; 1523f721889fSRafael J. Wysocki 1524f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1525f721889fSRafael J. Wysocki 1526efa69025SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) 1527efa69025SRafael J. Wysocki || IS_ERR_OR_NULL(dev->pm_domain) 1528efa69025SRafael J. Wysocki || pd_to_genpd(dev->pm_domain) != genpd) 1529f721889fSRafael J. Wysocki return -EINVAL; 1530f721889fSRafael J. Wysocki 153117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1532f721889fSRafael J. Wysocki 1533596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1534596ba34bSRafael J. Wysocki ret = -EAGAIN; 1535596ba34bSRafael J. Wysocki goto out; 1536596ba34bSRafael J. Wysocki } 1537596ba34bSRafael J. Wysocki 15386ff7bb0dSRafael J. Wysocki genpd->device_count--; 15396ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 15406ff7bb0dSRafael J. Wysocki 15416ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 15421d5fcfecSRafael J. Wysocki 1543f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1544efa69025SRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1545efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 15461d5fcfecSRafael J. Wysocki gpd_data = to_gpd_data(pdd); 15471d5fcfecSRafael J. Wysocki if (--gpd_data->refcount == 0) { 1548efa69025SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 15491d5fcfecSRafael J. Wysocki remove = true; 15501d5fcfecSRafael J. Wysocki } 15511d5fcfecSRafael J. Wysocki 15526ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1553f721889fSRafael J. Wysocki 15546ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 15556ff7bb0dSRafael J. Wysocki pdd->dev = NULL; 15566ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 15576ff7bb0dSRafael J. Wysocki 15586ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 15596ff7bb0dSRafael J. Wysocki 15606ff7bb0dSRafael J. Wysocki dev_pm_put_subsys_data(dev); 15611d5fcfecSRafael J. Wysocki if (remove) 15621d5fcfecSRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data); 15631d5fcfecSRafael J. Wysocki 15646ff7bb0dSRafael J. Wysocki return 0; 1565f721889fSRafael J. Wysocki 1566596ba34bSRafael J. Wysocki out: 156717b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1568f721889fSRafael J. Wysocki 1569f721889fSRafael J. Wysocki return ret; 1570f721889fSRafael J. Wysocki } 1571f721889fSRafael J. Wysocki 1572f721889fSRafael J. Wysocki /** 1573ca1d72f0SRafael J. Wysocki * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. 1574ca1d72f0SRafael J. Wysocki * @dev: Device to set/unset the flag for. 1575ca1d72f0SRafael J. Wysocki * @val: The new value of the device's "need restore" flag. 1576ca1d72f0SRafael J. Wysocki */ 1577ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val) 1578ca1d72f0SRafael J. Wysocki { 1579ca1d72f0SRafael J. Wysocki struct pm_subsys_data *psd; 1580ca1d72f0SRafael J. Wysocki unsigned long flags; 1581ca1d72f0SRafael J. Wysocki 1582ca1d72f0SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 1583ca1d72f0SRafael J. Wysocki 1584ca1d72f0SRafael J. Wysocki psd = dev_to_psd(dev); 1585ca1d72f0SRafael J. Wysocki if (psd && psd->domain_data) 1586ca1d72f0SRafael J. Wysocki to_gpd_data(psd->domain_data)->need_restore = val; 1587ca1d72f0SRafael J. Wysocki 1588ca1d72f0SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 1589ca1d72f0SRafael J. Wysocki } 1590ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); 1591ca1d72f0SRafael J. Wysocki 1592ca1d72f0SRafael J. Wysocki /** 1593f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1594f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1595bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1596f721889fSRafael J. Wysocki */ 1597f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1598bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1599f721889fSRafael J. Wysocki { 16005063ce15SRafael J. Wysocki struct gpd_link *link; 1601f721889fSRafael J. Wysocki int ret = 0; 1602f721889fSRafael J. Wysocki 1603fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1604fb7268beSRafael J. Wysocki || genpd == subdomain) 1605f721889fSRafael J. Wysocki return -EINVAL; 1606f721889fSRafael J. Wysocki 160717b75ecaSRafael J. Wysocki start: 160817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1609bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1610f721889fSRafael J. Wysocki 1611bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1612bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1613bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 161417b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 161517b75ecaSRafael J. Wysocki goto start; 161617b75ecaSRafael J. Wysocki } 161717b75ecaSRafael J. Wysocki 161817b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1619bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1620f721889fSRafael J. Wysocki ret = -EINVAL; 1621f721889fSRafael J. Wysocki goto out; 1622f721889fSRafael J. Wysocki } 1623f721889fSRafael J. Wysocki 16244fcac10dSHuang Ying list_for_each_entry(link, &genpd->master_links, master_node) { 1625bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1626f721889fSRafael J. Wysocki ret = -EINVAL; 1627f721889fSRafael J. Wysocki goto out; 1628f721889fSRafael J. Wysocki } 1629f721889fSRafael J. Wysocki } 1630f721889fSRafael J. Wysocki 16315063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 16325063ce15SRafael J. Wysocki if (!link) { 16335063ce15SRafael J. Wysocki ret = -ENOMEM; 16345063ce15SRafael J. Wysocki goto out; 16355063ce15SRafael J. Wysocki } 16365063ce15SRafael J. Wysocki link->master = genpd; 16375063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1638bc0403ffSRafael J. Wysocki link->slave = subdomain; 1639bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1640bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1641c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1642f721889fSRafael J. Wysocki 1643f721889fSRafael J. Wysocki out: 1644bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 164517b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1646f721889fSRafael J. Wysocki 1647f721889fSRafael J. Wysocki return ret; 1648f721889fSRafael J. Wysocki } 1649f721889fSRafael J. Wysocki 1650f721889fSRafael J. Wysocki /** 1651fb7268beSRafael J. Wysocki * pm_genpd_add_subdomain_names - Add a subdomain to an I/O PM domain. 1652fb7268beSRafael J. Wysocki * @master_name: Name of the master PM domain to add the subdomain to. 1653fb7268beSRafael J. Wysocki * @subdomain_name: Name of the subdomain to be added. 1654fb7268beSRafael J. Wysocki */ 1655fb7268beSRafael J. Wysocki int pm_genpd_add_subdomain_names(const char *master_name, 1656fb7268beSRafael J. Wysocki const char *subdomain_name) 1657fb7268beSRafael J. Wysocki { 1658fb7268beSRafael J. Wysocki struct generic_pm_domain *master = NULL, *subdomain = NULL, *gpd; 1659fb7268beSRafael J. Wysocki 1660fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(master_name) || IS_ERR_OR_NULL(subdomain_name)) 1661fb7268beSRafael J. Wysocki return -EINVAL; 1662fb7268beSRafael J. Wysocki 1663fb7268beSRafael J. Wysocki mutex_lock(&gpd_list_lock); 1664fb7268beSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1665fb7268beSRafael J. Wysocki if (!master && !strcmp(gpd->name, master_name)) 1666fb7268beSRafael J. Wysocki master = gpd; 1667fb7268beSRafael J. Wysocki 1668fb7268beSRafael J. Wysocki if (!subdomain && !strcmp(gpd->name, subdomain_name)) 1669fb7268beSRafael J. Wysocki subdomain = gpd; 1670fb7268beSRafael J. Wysocki 1671fb7268beSRafael J. Wysocki if (master && subdomain) 1672fb7268beSRafael J. Wysocki break; 1673fb7268beSRafael J. Wysocki } 1674fb7268beSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 1675fb7268beSRafael J. Wysocki 1676fb7268beSRafael J. Wysocki return pm_genpd_add_subdomain(master, subdomain); 1677fb7268beSRafael J. Wysocki } 1678fb7268beSRafael J. Wysocki 1679fb7268beSRafael J. Wysocki /** 1680f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1681f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 16825063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1683f721889fSRafael J. Wysocki */ 1684f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 16855063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1686f721889fSRafael J. Wysocki { 16875063ce15SRafael J. Wysocki struct gpd_link *link; 1688f721889fSRafael J. Wysocki int ret = -EINVAL; 1689f721889fSRafael J. Wysocki 16905063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1691f721889fSRafael J. Wysocki return -EINVAL; 1692f721889fSRafael J. Wysocki 169317b75ecaSRafael J. Wysocki start: 169417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1695f721889fSRafael J. Wysocki 16965063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 16975063ce15SRafael J. Wysocki if (link->slave != subdomain) 1698f721889fSRafael J. Wysocki continue; 1699f721889fSRafael J. Wysocki 1700f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1701f721889fSRafael J. Wysocki 170217b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 170317b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 170417b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 170517b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 170617b75ecaSRafael J. Wysocki goto start; 170717b75ecaSRafael J. Wysocki } 170817b75ecaSRafael J. Wysocki 17095063ce15SRafael J. Wysocki list_del(&link->master_node); 17105063ce15SRafael J. Wysocki list_del(&link->slave_node); 17115063ce15SRafael J. Wysocki kfree(link); 171217b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1713f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1714f721889fSRafael J. Wysocki 1715f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1716f721889fSRafael J. Wysocki 1717f721889fSRafael J. Wysocki ret = 0; 1718f721889fSRafael J. Wysocki break; 1719f721889fSRafael J. Wysocki } 1720f721889fSRafael J. Wysocki 172117b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1722f721889fSRafael J. Wysocki 1723f721889fSRafael J. Wysocki return ret; 1724f721889fSRafael J. Wysocki } 1725f721889fSRafael J. Wysocki 1726f721889fSRafael J. Wysocki /** 1727d5e4cbfeSRafael J. Wysocki * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. 1728d5e4cbfeSRafael J. Wysocki * @dev: Device to add the callbacks to. 1729d5e4cbfeSRafael J. Wysocki * @ops: Set of callbacks to add. 1730b02c999aSRafael J. Wysocki * @td: Timing data to add to the device along with the callbacks (optional). 173162d44902SRafael J. Wysocki * 173262d44902SRafael J. Wysocki * Every call to this routine should be balanced with a call to 173362d44902SRafael J. Wysocki * __pm_genpd_remove_callbacks() and they must not be nested. 1734d5e4cbfeSRafael J. Wysocki */ 1735b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, 1736b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1737d5e4cbfeSRafael J. Wysocki { 173862d44902SRafael J. Wysocki struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; 1739d5e4cbfeSRafael J. Wysocki int ret = 0; 1740d5e4cbfeSRafael J. Wysocki 174162d44902SRafael J. Wysocki if (!(dev && ops)) 1742d5e4cbfeSRafael J. Wysocki return -EINVAL; 1743d5e4cbfeSRafael J. Wysocki 174462d44902SRafael J. Wysocki gpd_data_new = __pm_genpd_alloc_dev_data(dev); 174562d44902SRafael J. Wysocki if (!gpd_data_new) 174662d44902SRafael J. Wysocki return -ENOMEM; 174762d44902SRafael J. Wysocki 1748d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1749d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1750d5e4cbfeSRafael J. Wysocki 175162d44902SRafael J. Wysocki ret = dev_pm_get_subsys_data(dev); 175262d44902SRafael J. Wysocki if (ret) 175362d44902SRafael J. Wysocki goto out; 1754d5e4cbfeSRafael J. Wysocki 175562d44902SRafael J. Wysocki spin_lock_irq(&dev->power.lock); 175662d44902SRafael J. Wysocki 175762d44902SRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 175862d44902SRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 175962d44902SRafael J. Wysocki } else { 176062d44902SRafael J. Wysocki gpd_data = gpd_data_new; 176162d44902SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 176262d44902SRafael J. Wysocki } 176362d44902SRafael J. Wysocki gpd_data->refcount++; 1764d5e4cbfeSRafael J. Wysocki gpd_data->ops = *ops; 1765b02c999aSRafael J. Wysocki if (td) 1766b02c999aSRafael J. Wysocki gpd_data->td = *td; 1767d5e4cbfeSRafael J. Wysocki 176862d44902SRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 176962d44902SRafael J. Wysocki 177062d44902SRafael J. Wysocki out: 1771d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1772d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1773d5e4cbfeSRafael J. Wysocki 177462d44902SRafael J. Wysocki if (gpd_data != gpd_data_new) 177562d44902SRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data_new); 177662d44902SRafael J. Wysocki 1777d5e4cbfeSRafael J. Wysocki return ret; 1778d5e4cbfeSRafael J. Wysocki } 1779d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); 1780d5e4cbfeSRafael J. Wysocki 1781d5e4cbfeSRafael J. Wysocki /** 1782b02c999aSRafael J. Wysocki * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. 1783d5e4cbfeSRafael J. Wysocki * @dev: Device to remove the callbacks from. 1784b02c999aSRafael J. Wysocki * @clear_td: If set, clear the device's timing data too. 178562d44902SRafael J. Wysocki * 178662d44902SRafael J. Wysocki * This routine can only be called after pm_genpd_add_callbacks(). 1787d5e4cbfeSRafael J. Wysocki */ 1788b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) 1789d5e4cbfeSRafael J. Wysocki { 179062d44902SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = NULL; 179162d44902SRafael J. Wysocki bool remove = false; 1792d5e4cbfeSRafael J. Wysocki int ret = 0; 1793d5e4cbfeSRafael J. Wysocki 1794d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data)) 1795d5e4cbfeSRafael J. Wysocki return -EINVAL; 1796d5e4cbfeSRafael J. Wysocki 1797d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1798d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1799d5e4cbfeSRafael J. Wysocki 180062d44902SRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1801d5e4cbfeSRafael J. Wysocki 180262d44902SRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 180362d44902SRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 1804db79e53dSSachin Kamat gpd_data->ops = (struct gpd_dev_ops){ NULL }; 1805b02c999aSRafael J. Wysocki if (clear_td) 1806b02c999aSRafael J. Wysocki gpd_data->td = (struct gpd_timing_data){ 0 }; 180762d44902SRafael J. Wysocki 180862d44902SRafael J. Wysocki if (--gpd_data->refcount == 0) { 180962d44902SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 181062d44902SRafael J. Wysocki remove = true; 181162d44902SRafael J. Wysocki } 1812d5e4cbfeSRafael J. Wysocki } else { 1813d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1814d5e4cbfeSRafael J. Wysocki } 1815d5e4cbfeSRafael J. Wysocki 181662d44902SRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 181762d44902SRafael J. Wysocki 1818d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1819d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1820d5e4cbfeSRafael J. Wysocki 182162d44902SRafael J. Wysocki if (ret) 1822d5e4cbfeSRafael J. Wysocki return ret; 182362d44902SRafael J. Wysocki 182462d44902SRafael J. Wysocki dev_pm_put_subsys_data(dev); 182562d44902SRafael J. Wysocki if (remove) 182662d44902SRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data); 182762d44902SRafael J. Wysocki 182862d44902SRafael J. Wysocki return 0; 1829d5e4cbfeSRafael J. Wysocki } 1830b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); 1831d5e4cbfeSRafael J. Wysocki 183240114447SRafael J. Wysocki /** 183340114447SRafael J. Wysocki * pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle. 183440114447SRafael J. Wysocki * @genpd: PM domain to be connected with cpuidle. 183540114447SRafael J. Wysocki * @state: cpuidle state this domain can disable/enable. 183640114447SRafael J. Wysocki * 183740114447SRafael J. Wysocki * Make a PM domain behave as though it contained a CPU core, that is, instead 183840114447SRafael J. Wysocki * of calling its power down routine it will enable the given cpuidle state so 183940114447SRafael J. Wysocki * that the cpuidle subsystem can power it down (if possible and desirable). 184040114447SRafael J. Wysocki */ 184140114447SRafael J. Wysocki int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) 1842cbc9ef02SRafael J. Wysocki { 1843cbc9ef02SRafael J. Wysocki struct cpuidle_driver *cpuidle_drv; 1844cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1845cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1846cbc9ef02SRafael J. Wysocki int ret = 0; 1847cbc9ef02SRafael J. Wysocki 1848cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || state < 0) 1849cbc9ef02SRafael J. Wysocki return -EINVAL; 1850cbc9ef02SRafael J. Wysocki 1851cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1852cbc9ef02SRafael J. Wysocki 1853cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 1854cbc9ef02SRafael J. Wysocki ret = -EEXIST; 1855cbc9ef02SRafael J. Wysocki goto out; 1856cbc9ef02SRafael J. Wysocki } 1857cbc9ef02SRafael J. Wysocki cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); 1858cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1859cbc9ef02SRafael J. Wysocki ret = -ENOMEM; 1860cbc9ef02SRafael J. Wysocki goto out; 1861cbc9ef02SRafael J. Wysocki } 1862cbc9ef02SRafael J. Wysocki cpuidle_drv = cpuidle_driver_ref(); 1863cbc9ef02SRafael J. Wysocki if (!cpuidle_drv) { 1864cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1865debe081aSjhbird.choi@samsung.com goto err_drv; 1866cbc9ef02SRafael J. Wysocki } 1867cbc9ef02SRafael J. Wysocki if (cpuidle_drv->state_count <= state) { 1868cbc9ef02SRafael J. Wysocki ret = -EINVAL; 1869cbc9ef02SRafael J. Wysocki goto err; 1870cbc9ef02SRafael J. Wysocki } 1871cbc9ef02SRafael J. Wysocki idle_state = &cpuidle_drv->states[state]; 1872cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1873cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1874cbc9ef02SRafael J. Wysocki goto err; 1875cbc9ef02SRafael J. Wysocki } 1876cbc9ef02SRafael J. Wysocki cpu_data->idle_state = idle_state; 1877cbc9ef02SRafael J. Wysocki cpu_data->saved_exit_latency = idle_state->exit_latency; 1878cbc9ef02SRafael J. Wysocki genpd->cpu_data = cpu_data; 1879cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 1880cbc9ef02SRafael J. Wysocki 1881cbc9ef02SRafael J. Wysocki out: 1882cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1883cbc9ef02SRafael J. Wysocki return ret; 1884cbc9ef02SRafael J. Wysocki 1885cbc9ef02SRafael J. Wysocki err: 1886cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1887debe081aSjhbird.choi@samsung.com 1888debe081aSjhbird.choi@samsung.com err_drv: 1889debe081aSjhbird.choi@samsung.com kfree(cpu_data); 1890cbc9ef02SRafael J. Wysocki goto out; 1891cbc9ef02SRafael J. Wysocki } 1892cbc9ef02SRafael J. Wysocki 189340114447SRafael J. Wysocki /** 189474a2799aSRafael J. Wysocki * pm_genpd_name_attach_cpuidle - Find PM domain and connect cpuidle to it. 189574a2799aSRafael J. Wysocki * @name: Name of the domain to connect to cpuidle. 189674a2799aSRafael J. Wysocki * @state: cpuidle state this domain can manipulate. 189774a2799aSRafael J. Wysocki */ 189874a2799aSRafael J. Wysocki int pm_genpd_name_attach_cpuidle(const char *name, int state) 189974a2799aSRafael J. Wysocki { 190074a2799aSRafael J. Wysocki return pm_genpd_attach_cpuidle(pm_genpd_lookup_name(name), state); 190174a2799aSRafael J. Wysocki } 190274a2799aSRafael J. Wysocki 190374a2799aSRafael J. Wysocki /** 190440114447SRafael J. Wysocki * pm_genpd_detach_cpuidle - Remove the cpuidle connection from a PM domain. 190540114447SRafael J. Wysocki * @genpd: PM domain to remove the cpuidle connection from. 190640114447SRafael J. Wysocki * 190740114447SRafael J. Wysocki * Remove the cpuidle connection set up by pm_genpd_attach_cpuidle() from the 190840114447SRafael J. Wysocki * given PM domain. 190940114447SRafael J. Wysocki */ 191040114447SRafael J. Wysocki int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) 1911cbc9ef02SRafael J. Wysocki { 1912cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1913cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1914cbc9ef02SRafael J. Wysocki int ret = 0; 1915cbc9ef02SRafael J. Wysocki 1916cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1917cbc9ef02SRafael J. Wysocki return -EINVAL; 1918cbc9ef02SRafael J. Wysocki 1919cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1920cbc9ef02SRafael J. Wysocki 1921cbc9ef02SRafael J. Wysocki cpu_data = genpd->cpu_data; 1922cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1923cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1924cbc9ef02SRafael J. Wysocki goto out; 1925cbc9ef02SRafael J. Wysocki } 1926cbc9ef02SRafael J. Wysocki idle_state = cpu_data->idle_state; 1927cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1928cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1929cbc9ef02SRafael J. Wysocki goto out; 1930cbc9ef02SRafael J. Wysocki } 1931cbc9ef02SRafael J. Wysocki idle_state->exit_latency = cpu_data->saved_exit_latency; 1932cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1933cbc9ef02SRafael J. Wysocki genpd->cpu_data = NULL; 1934cbc9ef02SRafael J. Wysocki kfree(cpu_data); 1935cbc9ef02SRafael J. Wysocki 1936cbc9ef02SRafael J. Wysocki out: 1937cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1938cbc9ef02SRafael J. Wysocki return ret; 1939cbc9ef02SRafael J. Wysocki } 1940cbc9ef02SRafael J. Wysocki 194174a2799aSRafael J. Wysocki /** 194274a2799aSRafael J. Wysocki * pm_genpd_name_detach_cpuidle - Find PM domain and disconnect cpuidle from it. 194374a2799aSRafael J. Wysocki * @name: Name of the domain to disconnect cpuidle from. 194474a2799aSRafael J. Wysocki */ 194574a2799aSRafael J. Wysocki int pm_genpd_name_detach_cpuidle(const char *name) 194674a2799aSRafael J. Wysocki { 194774a2799aSRafael J. Wysocki return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name)); 194874a2799aSRafael J. Wysocki } 194974a2799aSRafael J. Wysocki 1950d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1951d23b9b00SRafael J. Wysocki 1952d5e4cbfeSRafael J. Wysocki /** 1953ecf00475SRafael J. Wysocki * pm_genpd_default_save_state - Default "save device state" for PM domians. 1954ecf00475SRafael J. Wysocki * @dev: Device to handle. 1955ecf00475SRafael J. Wysocki */ 1956ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1957ecf00475SRafael J. Wysocki { 1958ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1959ecf00475SRafael J. Wysocki 1960ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.save_state; 1961ecf00475SRafael J. Wysocki if (cb) 1962ecf00475SRafael J. Wysocki return cb(dev); 1963ecf00475SRafael J. Wysocki 19640b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 19650b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 19660b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 19670b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 19680b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 19690b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 19700b589741SRafael J. Wysocki else 19710b589741SRafael J. Wysocki cb = NULL; 1972ecf00475SRafael J. Wysocki 19730b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 19740b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 19750b589741SRafael J. Wysocki 19760b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1977ecf00475SRafael J. Wysocki } 1978ecf00475SRafael J. Wysocki 1979ecf00475SRafael J. Wysocki /** 1980ecf00475SRafael J. Wysocki * pm_genpd_default_restore_state - Default PM domians "restore device state". 1981ecf00475SRafael J. Wysocki * @dev: Device to handle. 1982ecf00475SRafael J. Wysocki */ 1983ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1984ecf00475SRafael J. Wysocki { 1985ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1986ecf00475SRafael J. Wysocki 1987ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.restore_state; 1988ecf00475SRafael J. Wysocki if (cb) 1989ecf00475SRafael J. Wysocki return cb(dev); 1990ecf00475SRafael J. Wysocki 19910b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 19920b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 19930b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 19940b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 19950b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 19960b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 19970b589741SRafael J. Wysocki else 19980b589741SRafael J. Wysocki cb = NULL; 1999ecf00475SRafael J. Wysocki 20000b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 20010b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 20020b589741SRafael J. Wysocki 20030b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 2004ecf00475SRafael J. Wysocki } 2005ecf00475SRafael J. Wysocki 20060f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 20070f1d6986SRafael J. Wysocki 2008ecf00475SRafael J. Wysocki /** 2009d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend - Default "device suspend" for PM domians. 2010d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2011d23b9b00SRafael J. Wysocki */ 2012d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev) 2013d23b9b00SRafael J. Wysocki { 2014c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend; 2015d23b9b00SRafael J. Wysocki 2016d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend(dev); 2017d23b9b00SRafael J. Wysocki } 2018d23b9b00SRafael J. Wysocki 2019d23b9b00SRafael J. Wysocki /** 2020d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. 2021d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2022d23b9b00SRafael J. Wysocki */ 2023d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev) 2024d23b9b00SRafael J. Wysocki { 2025c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; 2026d23b9b00SRafael J. Wysocki 20270496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend_late(dev); 2028d23b9b00SRafael J. Wysocki } 2029d23b9b00SRafael J. Wysocki 2030d23b9b00SRafael J. Wysocki /** 2031d23b9b00SRafael J. Wysocki * pm_genpd_default_resume_early - Default "early device resume" for PM domians. 2032d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2033d23b9b00SRafael J. Wysocki */ 2034d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev) 2035d23b9b00SRafael J. Wysocki { 2036c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; 2037d23b9b00SRafael J. Wysocki 20380496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume_early(dev); 2039d23b9b00SRafael J. Wysocki } 2040d23b9b00SRafael J. Wysocki 2041d23b9b00SRafael J. Wysocki /** 2042d23b9b00SRafael J. Wysocki * pm_genpd_default_resume - Default "device resume" for PM domians. 2043d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2044d23b9b00SRafael J. Wysocki */ 2045d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev) 2046d23b9b00SRafael J. Wysocki { 2047c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume; 2048d23b9b00SRafael J. Wysocki 2049d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume(dev); 2050d23b9b00SRafael J. Wysocki } 2051d23b9b00SRafael J. Wysocki 2052d23b9b00SRafael J. Wysocki /** 2053d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze - Default "device freeze" for PM domians. 2054d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2055d23b9b00SRafael J. Wysocki */ 2056d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev) 2057d23b9b00SRafael J. Wysocki { 2058d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; 2059d23b9b00SRafael J. Wysocki 2060d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze(dev); 2061d23b9b00SRafael J. Wysocki } 2062d23b9b00SRafael J. Wysocki 2063d23b9b00SRafael J. Wysocki /** 2064d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. 2065d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2066d23b9b00SRafael J. Wysocki */ 2067d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev) 2068d23b9b00SRafael J. Wysocki { 2069d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; 2070d23b9b00SRafael J. Wysocki 20710496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze_late(dev); 2072d23b9b00SRafael J. Wysocki } 2073d23b9b00SRafael J. Wysocki 2074d23b9b00SRafael J. Wysocki /** 2075d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. 2076d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2077d23b9b00SRafael J. Wysocki */ 2078d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev) 2079d23b9b00SRafael J. Wysocki { 2080d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; 2081d23b9b00SRafael J. Wysocki 20820496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw_early(dev); 2083d23b9b00SRafael J. Wysocki } 2084d23b9b00SRafael J. Wysocki 2085d23b9b00SRafael J. Wysocki /** 2086d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw - Default "device thaw" for PM domians. 2087d23b9b00SRafael J. Wysocki * @dev: Device to handle. 2088d23b9b00SRafael J. Wysocki */ 2089d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev) 2090d23b9b00SRafael J. Wysocki { 2091d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; 2092d23b9b00SRafael J. Wysocki 2093d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw(dev); 2094d23b9b00SRafael J. Wysocki } 2095d23b9b00SRafael J. Wysocki 20960f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 20970f1d6986SRafael J. Wysocki 20980f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend NULL 20990f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late NULL 21000f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early NULL 21010f1d6986SRafael J. Wysocki #define pm_genpd_default_resume NULL 21020f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze NULL 21030f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late NULL 21040f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early NULL 21050f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw NULL 21060f1d6986SRafael J. Wysocki 21070f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */ 21080f1d6986SRafael J. Wysocki 2109d23b9b00SRafael J. Wysocki /** 2110f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 2111f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 2112f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 2113f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 2114f721889fSRafael J. Wysocki */ 2115f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 2116f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 2117f721889fSRafael J. Wysocki { 2118f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 2119f721889fSRafael J. Wysocki return; 2120f721889fSRafael J. Wysocki 21215063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 21225063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 2123f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 2124f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 2125f721889fSRafael J. Wysocki genpd->gov = gov; 2126f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 2127f721889fSRafael J. Wysocki genpd->in_progress = 0; 2128c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 212917b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 213017b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 2131c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 2132c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 2133596ba34bSRafael J. Wysocki genpd->device_count = 0; 2134221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 21356ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2136f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 2137f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 2138f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 2139596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 2140596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 21410496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 2142596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 2143596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 21440496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 2145596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 2146596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 21470496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 2148596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 2149596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 21500496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 2151596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 2152d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 21530496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 2154d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 2155596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 21560496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 2157d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 2158596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 2159ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 2160ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 2161c9914854SRafael J. Wysocki genpd->dev_ops.suspend = pm_genpd_default_suspend; 2162c9914854SRafael J. Wysocki genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late; 2163c9914854SRafael J. Wysocki genpd->dev_ops.resume_early = pm_genpd_default_resume_early; 2164c9914854SRafael J. Wysocki genpd->dev_ops.resume = pm_genpd_default_resume; 2165d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze = pm_genpd_default_freeze; 2166d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; 2167d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; 2168d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw = pm_genpd_default_thaw; 21695125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 21705125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 21715125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 21725125bbf3SRafael J. Wysocki } 2173