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 565248051bSRafael J. Wysocki #ifdef CONFIG_PM 575248051bSRafael J. Wysocki 58b02c999aSRafael J. Wysocki struct generic_pm_domain *dev_to_genpd(struct device *dev) 595248051bSRafael J. Wysocki { 605248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 615248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 625248051bSRafael J. Wysocki 63596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 645248051bSRafael J. Wysocki } 65f721889fSRafael J. Wysocki 66d5e4cbfeSRafael J. Wysocki static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) 67d5e4cbfeSRafael J. Wysocki { 680140d8bdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev, 690140d8bdSRafael J. Wysocki stop_latency_ns, "stop"); 70d5e4cbfeSRafael J. Wysocki } 71d5e4cbfeSRafael J. Wysocki 72d5e4cbfeSRafael J. Wysocki static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) 73d5e4cbfeSRafael J. Wysocki { 740140d8bdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev, 750140d8bdSRafael J. Wysocki start_latency_ns, "start"); 76d5e4cbfeSRafael J. Wysocki } 77d5e4cbfeSRafael J. Wysocki 78ecf00475SRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) 79ecf00475SRafael J. Wysocki { 800140d8bdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, 810140d8bdSRafael J. Wysocki save_state_latency_ns, "state save"); 82ecf00475SRafael J. Wysocki } 83ecf00475SRafael J. Wysocki 84ecf00475SRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) 85ecf00475SRafael J. Wysocki { 860140d8bdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, 870140d8bdSRafael J. Wysocki restore_state_latency_ns, 880140d8bdSRafael J. Wysocki "state restore"); 89ecf00475SRafael J. Wysocki } 90ecf00475SRafael J. Wysocki 91c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 92f721889fSRafael J. Wysocki { 93c4bb3160SRafael J. Wysocki bool ret = false; 94c4bb3160SRafael J. Wysocki 95c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 96c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 97c4bb3160SRafael J. Wysocki 98c4bb3160SRafael J. Wysocki return ret; 99c4bb3160SRafael J. Wysocki } 100c4bb3160SRafael J. Wysocki 101c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 102c4bb3160SRafael J. Wysocki { 103c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 104c4bb3160SRafael J. Wysocki smp_mb__after_atomic_inc(); 105f721889fSRafael J. Wysocki } 106f721889fSRafael J. Wysocki 10717b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd) 10817b75ecaSRafael J. Wysocki { 10917b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 11017b75ecaSRafael J. Wysocki 11117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 11217b75ecaSRafael J. Wysocki /* 11317b75ecaSRafael J. Wysocki * Wait for the domain to transition into either the active, 11417b75ecaSRafael J. Wysocki * or the power off state. 11517b75ecaSRafael J. Wysocki */ 11617b75ecaSRafael J. Wysocki for (;;) { 11717b75ecaSRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 11817b75ecaSRafael J. Wysocki TASK_UNINTERRUPTIBLE); 119c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 120c6d22b37SRafael J. Wysocki || genpd->status == GPD_STATE_POWER_OFF) 12117b75ecaSRafael J. Wysocki break; 12217b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 12317b75ecaSRafael J. Wysocki 12417b75ecaSRafael J. Wysocki schedule(); 12517b75ecaSRafael J. Wysocki 12617b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 12717b75ecaSRafael J. Wysocki } 12817b75ecaSRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 12917b75ecaSRafael J. Wysocki } 13017b75ecaSRafael J. Wysocki 13117b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd) 13217b75ecaSRafael J. Wysocki { 13317b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 13417b75ecaSRafael J. Wysocki } 13517b75ecaSRafael J. Wysocki 136c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd) 137c6d22b37SRafael J. Wysocki { 138c6d22b37SRafael J. Wysocki if (genpd->resume_count == 0) 139c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 140c6d22b37SRafael J. Wysocki } 141c6d22b37SRafael J. Wysocki 142cbc9ef02SRafael J. Wysocki static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) 143cbc9ef02SRafael J. Wysocki { 144cbc9ef02SRafael J. Wysocki s64 usecs64; 145cbc9ef02SRafael J. Wysocki 146cbc9ef02SRafael J. Wysocki if (!genpd->cpu_data) 147cbc9ef02SRafael J. Wysocki return; 148cbc9ef02SRafael J. Wysocki 149cbc9ef02SRafael J. Wysocki usecs64 = genpd->power_on_latency_ns; 150cbc9ef02SRafael J. Wysocki do_div(usecs64, NSEC_PER_USEC); 151cbc9ef02SRafael J. Wysocki usecs64 += genpd->cpu_data->saved_exit_latency; 152cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->exit_latency = usecs64; 153cbc9ef02SRafael J. Wysocki } 154cbc9ef02SRafael J. Wysocki 155f721889fSRafael J. Wysocki /** 1565063ce15SRafael J. Wysocki * __pm_genpd_poweron - Restore power to a given PM domain and its masters. 1575248051bSRafael J. Wysocki * @genpd: PM domain to power up. 1585248051bSRafael J. Wysocki * 1595063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 1605248051bSRafael J. Wysocki * resume a device belonging to it. 1615248051bSRafael J. Wysocki */ 1623f241775SRafael J. Wysocki int __pm_genpd_poweron(struct generic_pm_domain *genpd) 1633f241775SRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 1645248051bSRafael J. Wysocki { 1655063ce15SRafael J. Wysocki struct gpd_link *link; 1663f241775SRafael J. Wysocki DEFINE_WAIT(wait); 1675248051bSRafael J. Wysocki int ret = 0; 1685248051bSRafael J. Wysocki 1695063ce15SRafael J. Wysocki /* If the domain's master is being waited for, we have to wait too. */ 1703f241775SRafael J. Wysocki for (;;) { 1713f241775SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 1723f241775SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 17317877eb5SRafael J. Wysocki if (genpd->status != GPD_STATE_WAIT_MASTER) 1743f241775SRafael J. Wysocki break; 1753f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 1763f241775SRafael J. Wysocki 1773f241775SRafael J. Wysocki schedule(); 1783f241775SRafael J. Wysocki 17917b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 1803f241775SRafael J. Wysocki } 1813f241775SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 18217b75ecaSRafael J. Wysocki 18317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 184596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1853f241775SRafael J. Wysocki return 0; 1865248051bSRafael J. Wysocki 187c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 188c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 1893f241775SRafael J. Wysocki return 0; 190c6d22b37SRafael J. Wysocki } 191c6d22b37SRafael J. Wysocki 192cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 193cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 194cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = true; 195cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 196cbc9ef02SRafael J. Wysocki goto out; 197cbc9ef02SRafael J. Wysocki } 198cbc9ef02SRafael J. Wysocki 1995063ce15SRafael J. Wysocki /* 2005063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 2015063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 2025063ce15SRafael J. Wysocki * with it. 2035063ce15SRafael J. Wysocki */ 2045063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 2055063ce15SRafael J. Wysocki genpd_sd_counter_inc(link->master); 20617877eb5SRafael J. Wysocki genpd->status = GPD_STATE_WAIT_MASTER; 2073c07cbc4SRafael J. Wysocki 2085248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 2095248051bSRafael J. Wysocki 2105063ce15SRafael J. Wysocki ret = pm_genpd_poweron(link->master); 2119e08cf42SRafael J. Wysocki 2129e08cf42SRafael J. Wysocki mutex_lock(&genpd->lock); 2139e08cf42SRafael J. Wysocki 2143f241775SRafael J. Wysocki /* 2153f241775SRafael J. Wysocki * The "wait for parent" status is guaranteed not to change 2165063ce15SRafael J. Wysocki * while the master is powering on. 2173f241775SRafael J. Wysocki */ 2183f241775SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 2193f241775SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 2205063ce15SRafael J. Wysocki if (ret) { 2215063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2229e08cf42SRafael J. Wysocki goto err; 2235248051bSRafael J. Wysocki } 2245063ce15SRafael J. Wysocki } 2255248051bSRafael J. Wysocki 2269e08cf42SRafael J. Wysocki if (genpd->power_on) { 2270140d8bdSRafael J. Wysocki ktime_t time_start = ktime_get(); 2280140d8bdSRafael J. Wysocki s64 elapsed_ns; 2290140d8bdSRafael J. Wysocki 230fe202fdeSRafael J. Wysocki ret = genpd->power_on(genpd); 2319e08cf42SRafael J. Wysocki if (ret) 2329e08cf42SRafael J. Wysocki goto err; 2330140d8bdSRafael J. Wysocki 2340140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 235e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_on_latency_ns) { 2360140d8bdSRafael J. Wysocki genpd->power_on_latency_ns = elapsed_ns; 2376ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 238cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 239e84b2c20SRafael J. Wysocki if (genpd->name) 240e84b2c20SRafael J. Wysocki pr_warning("%s: Power-on latency exceeded, " 241e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 242e84b2c20SRafael J. Wysocki elapsed_ns); 243e84b2c20SRafael J. Wysocki } 2443c07cbc4SRafael J. Wysocki } 2455248051bSRafael J. Wysocki 246cbc9ef02SRafael J. Wysocki out: 2479e08cf42SRafael J. Wysocki genpd_set_active(genpd); 2489e08cf42SRafael J. Wysocki 2493f241775SRafael J. Wysocki return 0; 2509e08cf42SRafael J. Wysocki 2519e08cf42SRafael J. Wysocki err: 2525063ce15SRafael J. Wysocki list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) 2535063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2549e08cf42SRafael J. Wysocki 2553f241775SRafael J. Wysocki return ret; 2563f241775SRafael J. Wysocki } 2573f241775SRafael J. Wysocki 2583f241775SRafael J. Wysocki /** 2595063ce15SRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its masters. 2603f241775SRafael J. Wysocki * @genpd: PM domain to power up. 2613f241775SRafael J. Wysocki */ 2623f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd) 2633f241775SRafael J. Wysocki { 2643f241775SRafael J. Wysocki int ret; 2653f241775SRafael J. Wysocki 2663f241775SRafael J. Wysocki mutex_lock(&genpd->lock); 2673f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 2683f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 2693f241775SRafael J. Wysocki return ret; 2705248051bSRafael J. Wysocki } 2715248051bSRafael J. Wysocki 2725248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 2735248051bSRafael J. Wysocki 2745248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 2755248051bSRafael J. Wysocki 2766ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 2776ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 2786ff7bb0dSRafael J. Wysocki { 2796ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 2806ff7bb0dSRafael J. Wysocki struct device *dev; 2816ff7bb0dSRafael J. Wysocki 2826ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 2836ff7bb0dSRafael J. Wysocki 2846ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 2856ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 2866ff7bb0dSRafael J. Wysocki if (!dev) { 2876ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 2886ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2896ff7bb0dSRafael J. Wysocki } 2906ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 2916ff7bb0dSRafael J. Wysocki 2926ff7bb0dSRafael J. Wysocki for (;;) { 2936ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 2946ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 2956ff7bb0dSRafael J. Wysocki 2966ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 2976ff7bb0dSRafael J. Wysocki 2986ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 2996ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 3006ff7bb0dSRafael J. Wysocki if (pdd) { 3016ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 3026ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 3036ff7bb0dSRafael J. Wysocki } else { 3046ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 3056ff7bb0dSRafael J. Wysocki } 3066ff7bb0dSRafael J. Wysocki 3076ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 3086ff7bb0dSRafael J. Wysocki 3096ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 3106ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 3116ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 3126ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 3136ff7bb0dSRafael J. Wysocki } 3146ff7bb0dSRafael J. Wysocki 3156ff7bb0dSRafael J. Wysocki dev = dev->parent; 3166ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 3176ff7bb0dSRafael J. Wysocki break; 3186ff7bb0dSRafael J. Wysocki } 3196ff7bb0dSRafael J. Wysocki 3206ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 3216ff7bb0dSRafael J. Wysocki } 3226ff7bb0dSRafael J. Wysocki 3235248051bSRafael J. Wysocki /** 324f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 3254605ab65SRafael J. Wysocki * @pdd: Domain data of the device to save the state of. 326f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 327f721889fSRafael J. Wysocki */ 3284605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd, 329f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 33017b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 331f721889fSRafael J. Wysocki { 332cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3334605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 334f721889fSRafael J. Wysocki int ret = 0; 335f721889fSRafael J. Wysocki 336cd0ea672SRafael J. Wysocki if (gpd_data->need_restore) 337f721889fSRafael J. Wysocki return 0; 338f721889fSRafael J. Wysocki 33917b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 34017b75ecaSRafael J. Wysocki 341d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 342ecf00475SRafael J. Wysocki ret = genpd_save_dev(genpd, dev); 343d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 344f721889fSRafael J. Wysocki 34517b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 34617b75ecaSRafael J. Wysocki 347f721889fSRafael J. Wysocki if (!ret) 348cd0ea672SRafael J. Wysocki gpd_data->need_restore = true; 349f721889fSRafael J. Wysocki 350f721889fSRafael J. Wysocki return ret; 351f721889fSRafael J. Wysocki } 352f721889fSRafael J. Wysocki 353f721889fSRafael J. Wysocki /** 354f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 3554605ab65SRafael J. Wysocki * @pdd: Domain data of the device to restore the state of. 356f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 357f721889fSRafael J. Wysocki */ 3584605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd, 359f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 36017b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 361f721889fSRafael J. Wysocki { 362cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3634605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 36480de3d7fSRafael J. Wysocki bool need_restore = gpd_data->need_restore; 365f721889fSRafael J. Wysocki 36680de3d7fSRafael J. Wysocki gpd_data->need_restore = false; 36717b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 36817b75ecaSRafael J. Wysocki 369d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 37080de3d7fSRafael J. Wysocki if (need_restore) 371ecf00475SRafael J. Wysocki genpd_restore_dev(genpd, dev); 372f721889fSRafael J. Wysocki 37317b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 374f721889fSRafael J. Wysocki } 375f721889fSRafael J. Wysocki 376f721889fSRafael J. Wysocki /** 377c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 378c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 379c6d22b37SRafael J. Wysocki * 380c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 381c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 382c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 383c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 384c6d22b37SRafael J. Wysocki */ 385c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 386c6d22b37SRafael J. Wysocki { 38717877eb5SRafael J. Wysocki return genpd->status == GPD_STATE_WAIT_MASTER 3883f241775SRafael J. Wysocki || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 389c6d22b37SRafael J. Wysocki } 390c6d22b37SRafael J. Wysocki 391c6d22b37SRafael J. Wysocki /** 39256375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 39356375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 39456375fd4SRafael J. Wysocki * 39556375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 39656375fd4SRafael J. Wysocki * before. 39756375fd4SRafael J. Wysocki */ 3980bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 39956375fd4SRafael J. Wysocki { 40056375fd4SRafael J. Wysocki if (!work_pending(&genpd->power_off_work)) 40156375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 40256375fd4SRafael J. Wysocki } 40356375fd4SRafael J. Wysocki 40456375fd4SRafael J. Wysocki /** 405f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 406f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 407f721889fSRafael J. Wysocki * 408f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 409f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 410f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 411f721889fSRafael J. Wysocki */ 412f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 41317b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 414f721889fSRafael J. Wysocki { 4154605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 4165063ce15SRafael J. Wysocki struct gpd_link *link; 417f721889fSRafael J. Wysocki unsigned int not_suspended; 418c6d22b37SRafael J. Wysocki int ret = 0; 419f721889fSRafael J. Wysocki 420c6d22b37SRafael J. Wysocki start: 421c6d22b37SRafael J. Wysocki /* 422c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 423c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 4245063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 425c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 4263f241775SRafael J. Wysocki * (4) System suspend is in progress. 427c6d22b37SRafael J. Wysocki */ 4283f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 42917877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 4303f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 431f721889fSRafael J. Wysocki return 0; 432f721889fSRafael J. Wysocki 433c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 434f721889fSRafael J. Wysocki return -EBUSY; 435f721889fSRafael J. Wysocki 436f721889fSRafael J. Wysocki not_suspended = 0; 4374605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 4380aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 4391e78a0c7SRafael J. Wysocki || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on)) 440f721889fSRafael J. Wysocki not_suspended++; 441f721889fSRafael J. Wysocki 442f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 443f721889fSRafael J. Wysocki return -EBUSY; 444f721889fSRafael J. Wysocki 445c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 446c6d22b37SRafael J. Wysocki /* 447c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 448c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 449c6d22b37SRafael J. Wysocki */ 450c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 451c6d22b37SRafael J. Wysocki return 0; 452c6d22b37SRafael J. Wysocki } 453c6d22b37SRafael J. Wysocki 454f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 455f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 456f721889fSRafael J. Wysocki return -EAGAIN; 457f721889fSRafael J. Wysocki } 458f721889fSRafael J. Wysocki 45917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 460c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 46117b75ecaSRafael J. Wysocki 4624605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 4633c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 4644605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 4653f241775SRafael J. Wysocki 4663f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 4673f241775SRafael J. Wysocki goto out; 4683f241775SRafael J. Wysocki 469697a7f37SRafael J. Wysocki if (ret) { 470697a7f37SRafael J. Wysocki genpd_set_active(genpd); 471697a7f37SRafael J. Wysocki goto out; 472697a7f37SRafael J. Wysocki } 473f721889fSRafael J. Wysocki 474c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 475c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 476c6d22b37SRafael J. Wysocki goto start; 477c6d22b37SRafael J. Wysocki } 478c6d22b37SRafael J. Wysocki } 47917b75ecaSRafael J. Wysocki 480cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 481cbc9ef02SRafael J. Wysocki /* 482cbc9ef02SRafael J. Wysocki * If cpu_data is set, cpuidle should turn the domain off when 483cbc9ef02SRafael J. Wysocki * the CPU in it is idle. In that case we don't decrement the 484cbc9ef02SRafael J. Wysocki * subdomain counts of the master domains, so that power is not 485cbc9ef02SRafael J. Wysocki * removed from the current domain prematurely as a result of 486cbc9ef02SRafael J. Wysocki * cutting off the masters' power. 487cbc9ef02SRafael J. Wysocki */ 488cbc9ef02SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 489cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 490cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = false; 491cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 492cbc9ef02SRafael J. Wysocki goto out; 493cbc9ef02SRafael J. Wysocki } 494cbc9ef02SRafael J. Wysocki 4953c07cbc4SRafael J. Wysocki if (genpd->power_off) { 4960140d8bdSRafael J. Wysocki ktime_t time_start; 4970140d8bdSRafael J. Wysocki s64 elapsed_ns; 4980140d8bdSRafael J. Wysocki 4993c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 5003c07cbc4SRafael J. Wysocki ret = -EBUSY; 501c6d22b37SRafael J. Wysocki goto out; 502c6d22b37SRafael J. Wysocki } 50317b75ecaSRafael J. Wysocki 5040140d8bdSRafael J. Wysocki time_start = ktime_get(); 5050140d8bdSRafael J. Wysocki 5063c07cbc4SRafael J. Wysocki /* 5075063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 5085063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 5093c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 5103c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 5113c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 5123c07cbc4SRafael J. Wysocki * happen very often). 5133c07cbc4SRafael J. Wysocki */ 514d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 515d2805402SRafael J. Wysocki if (ret == -EBUSY) { 516d2805402SRafael J. Wysocki genpd_set_active(genpd); 517d2805402SRafael J. Wysocki goto out; 518d2805402SRafael J. Wysocki } 5190140d8bdSRafael J. Wysocki 5200140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 521e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_off_latency_ns) { 5220140d8bdSRafael J. Wysocki genpd->power_off_latency_ns = elapsed_ns; 5236ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 524e84b2c20SRafael J. Wysocki if (genpd->name) 525e84b2c20SRafael J. Wysocki pr_warning("%s: Power-off latency exceeded, " 526e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 527e84b2c20SRafael J. Wysocki elapsed_ns); 528e84b2c20SRafael J. Wysocki } 529d2805402SRafael J. Wysocki } 530f721889fSRafael J. Wysocki 53117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 532221e9b58SRafael J. Wysocki 5335063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5345063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 5355063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 5365063ce15SRafael J. Wysocki } 53717b75ecaSRafael J. Wysocki 538c6d22b37SRafael J. Wysocki out: 539c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 540c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 541c6d22b37SRafael J. Wysocki return ret; 542f721889fSRafael J. Wysocki } 543f721889fSRafael J. Wysocki 544f721889fSRafael J. Wysocki /** 545f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 546f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 547f721889fSRafael J. Wysocki */ 548f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 549f721889fSRafael J. Wysocki { 550f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 551f721889fSRafael J. Wysocki 552f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 553f721889fSRafael J. Wysocki 55417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 555f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 55617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 557f721889fSRafael J. Wysocki } 558f721889fSRafael J. Wysocki 559f721889fSRafael J. Wysocki /** 560f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 561f721889fSRafael J. Wysocki * @dev: Device to suspend. 562f721889fSRafael J. Wysocki * 563f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 564f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 565f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 566f721889fSRafael J. Wysocki */ 567f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 568f721889fSRafael J. Wysocki { 569f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 570b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 571d5e4cbfeSRafael J. Wysocki int ret; 572f721889fSRafael J. Wysocki 573f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 574f721889fSRafael J. Wysocki 5755248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5765248051bSRafael J. Wysocki if (IS_ERR(genpd)) 577f721889fSRafael J. Wysocki return -EINVAL; 578f721889fSRafael J. Wysocki 5790aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 5800aa2a221SRafael J. Wysocki 5811e78a0c7SRafael J. Wysocki if (dev_gpd_data(dev)->always_on) 5821e78a0c7SRafael J. Wysocki return -EBUSY; 5831e78a0c7SRafael J. Wysocki 584b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 585b02c999aSRafael J. Wysocki if (stop_ok && !stop_ok(dev)) 586b02c999aSRafael J. Wysocki return -EBUSY; 587b02c999aSRafael J. Wysocki 588d5e4cbfeSRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 589f721889fSRafael J. Wysocki if (ret) 59017b75ecaSRafael J. Wysocki return ret; 59117b75ecaSRafael J. Wysocki 5920aa2a221SRafael J. Wysocki /* 5930aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 5940aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 5950aa2a221SRafael J. Wysocki */ 5960aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 5970aa2a221SRafael J. Wysocki return 0; 5980aa2a221SRafael J. Wysocki 599c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 600f721889fSRafael J. Wysocki genpd->in_progress++; 601f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 602f721889fSRafael J. Wysocki genpd->in_progress--; 603c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 604f721889fSRafael J. Wysocki 605f721889fSRafael J. Wysocki return 0; 606f721889fSRafael J. Wysocki } 607f721889fSRafael J. Wysocki 608f721889fSRafael J. Wysocki /** 609f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 610f721889fSRafael J. Wysocki * @dev: Device to resume. 611f721889fSRafael J. Wysocki * 612f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 613f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 614f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 615f721889fSRafael J. Wysocki */ 616f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 617f721889fSRafael J. Wysocki { 618f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 619c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 620f721889fSRafael J. Wysocki int ret; 621f721889fSRafael J. Wysocki 622f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 623f721889fSRafael J. Wysocki 6245248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6255248051bSRafael J. Wysocki if (IS_ERR(genpd)) 626f721889fSRafael J. Wysocki return -EINVAL; 627f721889fSRafael J. Wysocki 6280aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 6290aa2a221SRafael J. Wysocki 6300aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 6310aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 63280de3d7fSRafael J. Wysocki return genpd_start_dev(genpd, dev); 6330aa2a221SRafael J. Wysocki 634c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 6353f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 6363f241775SRafael J. Wysocki if (ret) { 6373f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 6383f241775SRafael J. Wysocki return ret; 6393f241775SRafael J. Wysocki } 64017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 641c6d22b37SRafael J. Wysocki genpd->resume_count++; 642c6d22b37SRafael J. Wysocki for (;;) { 643c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 644c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 645c6d22b37SRafael J. Wysocki /* 646c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 647c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 648c6d22b37SRafael J. Wysocki * not wait. 649c6d22b37SRafael J. Wysocki */ 650c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 651c6d22b37SRafael J. Wysocki break; 652c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 653c6d22b37SRafael J. Wysocki 654c6d22b37SRafael J. Wysocki schedule(); 655c6d22b37SRafael J. Wysocki 656c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 657c6d22b37SRafael J. Wysocki } 658c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 659cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 660c6d22b37SRafael J. Wysocki genpd->resume_count--; 661c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 66217b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 663c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 66417b75ecaSRafael J. Wysocki 665f721889fSRafael J. Wysocki return 0; 666f721889fSRafael J. Wysocki } 667f721889fSRafael J. Wysocki 66817f2ae7fSRafael J. Wysocki /** 66917f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 67017f2ae7fSRafael J. Wysocki */ 67117f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 67217f2ae7fSRafael J. Wysocki { 67317f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 67417f2ae7fSRafael J. Wysocki 67517f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 67617f2ae7fSRafael J. Wysocki 67717f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 67817f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 67917f2ae7fSRafael J. Wysocki 68017f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 68117f2ae7fSRafael J. Wysocki } 68217f2ae7fSRafael J. Wysocki 683f721889fSRafael J. Wysocki #else 684f721889fSRafael J. Wysocki 6856ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 6866ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 6876ff7bb0dSRafael J. Wysocki { 6886ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 6896ff7bb0dSRafael J. Wysocki } 6906ff7bb0dSRafael J. Wysocki 691f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 692f721889fSRafael J. Wysocki 693f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 694f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 695f721889fSRafael J. Wysocki 696f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 697f721889fSRafael J. Wysocki 698596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 699596ba34bSRafael J. Wysocki 700d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 701d5e4cbfeSRafael J. Wysocki struct device *dev) 702d5e4cbfeSRafael J. Wysocki { 703d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 704d5e4cbfeSRafael J. Wysocki } 705d5e4cbfeSRafael J. Wysocki 706d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) 707d23b9b00SRafael J. Wysocki { 708d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); 709d23b9b00SRafael J. Wysocki } 710d23b9b00SRafael J. Wysocki 711d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) 712d23b9b00SRafael J. Wysocki { 713d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); 714d23b9b00SRafael J. Wysocki } 715d23b9b00SRafael J. Wysocki 716d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) 717d23b9b00SRafael J. Wysocki { 718d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); 719d23b9b00SRafael J. Wysocki } 720d23b9b00SRafael J. Wysocki 721d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) 722d23b9b00SRafael J. Wysocki { 723d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume, dev); 724d23b9b00SRafael J. Wysocki } 725d23b9b00SRafael J. Wysocki 726d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) 727d23b9b00SRafael J. Wysocki { 728d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); 729d23b9b00SRafael J. Wysocki } 730d23b9b00SRafael J. Wysocki 731d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) 732d23b9b00SRafael J. Wysocki { 733d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); 734d23b9b00SRafael J. Wysocki } 735d23b9b00SRafael J. Wysocki 736d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) 737d23b9b00SRafael J. Wysocki { 738d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); 739d23b9b00SRafael J. Wysocki } 740d23b9b00SRafael J. Wysocki 741d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) 742d23b9b00SRafael J. Wysocki { 743d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); 744d23b9b00SRafael J. Wysocki } 745d23b9b00SRafael J. Wysocki 746596ba34bSRafael J. Wysocki /** 7475063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 748596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 749596ba34bSRafael J. Wysocki * 750596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7515063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 752596ba34bSRafael J. Wysocki * 753596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 754596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 755596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 756596ba34bSRafael J. Wysocki */ 757596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 758596ba34bSRafael J. Wysocki { 7595063ce15SRafael J. Wysocki struct gpd_link *link; 760596ba34bSRafael J. Wysocki 76117b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 762596ba34bSRafael J. Wysocki return; 763596ba34bSRafael J. Wysocki 764c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 765c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 766596ba34bSRafael J. Wysocki return; 767596ba34bSRafael J. Wysocki 768596ba34bSRafael J. Wysocki if (genpd->power_off) 769596ba34bSRafael J. Wysocki genpd->power_off(genpd); 770596ba34bSRafael J. Wysocki 77117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7725063ce15SRafael J. Wysocki 7735063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7745063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 7755063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 776596ba34bSRafael J. Wysocki } 777596ba34bSRafael J. Wysocki } 778596ba34bSRafael J. Wysocki 779596ba34bSRafael J. Wysocki /** 7804ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 7814ecd6e65SRafael J. Wysocki * @dev: Device to check. 7824ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 7834ecd6e65SRafael J. Wysocki * 7844ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 7854ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 7864ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 7874ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 7884ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 7894ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 7904ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 7914ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 7924ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 7934ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 7944ecd6e65SRafael J. Wysocki */ 7954ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 7964ecd6e65SRafael J. Wysocki { 7974ecd6e65SRafael J. Wysocki bool active_wakeup; 7984ecd6e65SRafael J. Wysocki 7994ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8004ecd6e65SRafael J. Wysocki return false; 8014ecd6e65SRafael J. Wysocki 802d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8034ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8044ecd6e65SRafael J. Wysocki } 8054ecd6e65SRafael J. Wysocki 8064ecd6e65SRafael J. Wysocki /** 807596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 808596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 809596ba34bSRafael J. Wysocki * 810596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 811596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 812596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 813596ba34bSRafael J. Wysocki * consisting of I/O devices. 814596ba34bSRafael J. Wysocki */ 815596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 816596ba34bSRafael J. Wysocki { 817596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 818b6c10c84SRafael J. Wysocki int ret; 819596ba34bSRafael J. Wysocki 820596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 821596ba34bSRafael J. Wysocki 822596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 823596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 824596ba34bSRafael J. Wysocki return -EINVAL; 825596ba34bSRafael J. Wysocki 82617b75ecaSRafael J. Wysocki /* 82717b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 82817b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 82917b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 83017b75ecaSRafael J. Wysocki */ 83117b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 83217b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 83317b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 83417b75ecaSRafael J. Wysocki 83517b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 83617b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 83717b75ecaSRafael J. Wysocki return -EBUSY; 83817b75ecaSRafael J. Wysocki } 83917b75ecaSRafael J. Wysocki 8404ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8414ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8424ecd6e65SRafael J. Wysocki 84317b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 844596ba34bSRafael J. Wysocki 84565533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 84665533bbfSRafael J. Wysocki genpd->suspended_count = 0; 84717b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 84865533bbfSRafael J. Wysocki } 84917b75ecaSRafael J. Wysocki 85017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 851596ba34bSRafael J. Wysocki 852596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 85317b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 854596ba34bSRafael J. Wysocki return 0; 855596ba34bSRafael J. Wysocki } 856596ba34bSRafael J. Wysocki 857596ba34bSRafael J. Wysocki /* 85817b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 85917b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 860d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 86117b75ecaSRafael J. Wysocki * to make it operational. 862596ba34bSRafael J. Wysocki */ 86317b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 864596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 865596ba34bSRafael J. Wysocki 866b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 867b6c10c84SRafael J. Wysocki if (ret) { 868b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 869b6c10c84SRafael J. Wysocki 870b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 871b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 872b6c10c84SRafael J. Wysocki 873b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 87417b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 875b6c10c84SRafael J. Wysocki } 87617b75ecaSRafael J. Wysocki 87717b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 878b6c10c84SRafael J. Wysocki return ret; 879596ba34bSRafael J. Wysocki } 880596ba34bSRafael J. Wysocki 881596ba34bSRafael J. Wysocki /** 882596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 883596ba34bSRafael J. Wysocki * @dev: Device to suspend. 884596ba34bSRafael J. Wysocki * 885596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 886596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 887596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 888596ba34bSRafael J. Wysocki */ 889596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 890596ba34bSRafael J. Wysocki { 891596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 892596ba34bSRafael J. Wysocki 893596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 894596ba34bSRafael J. Wysocki 895596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 896596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 897596ba34bSRafael J. Wysocki return -EINVAL; 898596ba34bSRafael J. Wysocki 899d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); 900596ba34bSRafael J. Wysocki } 901596ba34bSRafael J. Wysocki 902596ba34bSRafael J. Wysocki /** 9030496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 904596ba34bSRafael J. Wysocki * @dev: Device to suspend. 905596ba34bSRafael J. Wysocki * 906596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 907596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 908596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 909596ba34bSRafael J. Wysocki */ 9100496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 911596ba34bSRafael J. Wysocki { 912596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 913596ba34bSRafael J. Wysocki 914596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 915596ba34bSRafael J. Wysocki 916596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 917596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 918596ba34bSRafael J. Wysocki return -EINVAL; 919596ba34bSRafael J. Wysocki 9200496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); 9210496c8aeSRafael J. Wysocki } 922596ba34bSRafael J. Wysocki 9230496c8aeSRafael J. Wysocki /** 9240496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 9250496c8aeSRafael J. Wysocki * @dev: Device to suspend. 9260496c8aeSRafael J. Wysocki * 9270496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 9280496c8aeSRafael J. Wysocki * been stopped. 9290496c8aeSRafael J. Wysocki */ 9300496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 9310496c8aeSRafael J. Wysocki { 9320496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 933596ba34bSRafael J. Wysocki 9340496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9350496c8aeSRafael J. Wysocki 9360496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9370496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9380496c8aeSRafael J. Wysocki return -EINVAL; 9390496c8aeSRafael J. Wysocki 9401e78a0c7SRafael J. Wysocki if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on 9410496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 942d4f2d87aSRafael J. Wysocki return 0; 943d4f2d87aSRafael J. Wysocki 944d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 945596ba34bSRafael J. Wysocki 946596ba34bSRafael J. Wysocki /* 947596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 948596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 949596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 950596ba34bSRafael J. Wysocki */ 951596ba34bSRafael J. Wysocki genpd->suspended_count++; 952596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 953596ba34bSRafael J. Wysocki 954596ba34bSRafael J. Wysocki return 0; 955596ba34bSRafael J. Wysocki } 956596ba34bSRafael J. Wysocki 957596ba34bSRafael J. Wysocki /** 9580496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 959596ba34bSRafael J. Wysocki * @dev: Device to resume. 960596ba34bSRafael J. Wysocki * 9610496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 962596ba34bSRafael J. Wysocki */ 963596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 964596ba34bSRafael J. Wysocki { 965596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 966596ba34bSRafael J. Wysocki 967596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 968596ba34bSRafael J. Wysocki 969596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 970596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 971596ba34bSRafael J. Wysocki return -EINVAL; 972596ba34bSRafael J. Wysocki 9731e78a0c7SRafael J. Wysocki if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on 974cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 975596ba34bSRafael J. Wysocki return 0; 976596ba34bSRafael J. Wysocki 977596ba34bSRafael J. Wysocki /* 978596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 979596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 980596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 981596ba34bSRafael J. Wysocki */ 982596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 983596ba34bSRafael J. Wysocki genpd->suspended_count--; 984596ba34bSRafael J. Wysocki 9850496c8aeSRafael J. Wysocki return genpd_start_dev(genpd, dev); 986596ba34bSRafael J. Wysocki } 987596ba34bSRafael J. Wysocki 988596ba34bSRafael J. Wysocki /** 9890496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 9900496c8aeSRafael J. Wysocki * @dev: Device to resume. 9910496c8aeSRafael J. Wysocki * 9920496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 9930496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9940496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9950496c8aeSRafael J. Wysocki * devices. 9960496c8aeSRafael J. Wysocki */ 9970496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 9980496c8aeSRafael J. Wysocki { 9990496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10000496c8aeSRafael J. Wysocki 10010496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10020496c8aeSRafael J. Wysocki 10030496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10040496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10050496c8aeSRafael J. Wysocki return -EINVAL; 10060496c8aeSRafael J. Wysocki 10070496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); 10080496c8aeSRafael J. Wysocki } 10090496c8aeSRafael J. Wysocki 10100496c8aeSRafael J. Wysocki /** 10110496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 1012596ba34bSRafael J. Wysocki * @dev: Device to resume. 1013596ba34bSRafael J. Wysocki * 1014596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 1015596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1016596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1017596ba34bSRafael J. Wysocki */ 1018596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 1019596ba34bSRafael J. Wysocki { 1020596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1021596ba34bSRafael J. Wysocki 1022596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1023596ba34bSRafael J. Wysocki 1024596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1025596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1026596ba34bSRafael J. Wysocki return -EINVAL; 1027596ba34bSRafael J. Wysocki 1028d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); 1029596ba34bSRafael J. Wysocki } 1030596ba34bSRafael J. Wysocki 1031596ba34bSRafael J. Wysocki /** 10320496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 1033596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1034596ba34bSRafael J. Wysocki * 1035596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 1036596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1037596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1038596ba34bSRafael J. Wysocki */ 1039596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 1040596ba34bSRafael J. Wysocki { 1041596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1042596ba34bSRafael J. Wysocki 1043596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1044596ba34bSRafael J. Wysocki 1045596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1046596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1047596ba34bSRafael J. Wysocki return -EINVAL; 1048596ba34bSRafael J. Wysocki 1049d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); 1050596ba34bSRafael J. Wysocki } 1051596ba34bSRafael J. Wysocki 1052596ba34bSRafael J. Wysocki /** 10530496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 10540496c8aeSRafael J. Wysocki * @dev: Device to freeze. 10550496c8aeSRafael J. Wysocki * 10560496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 10570496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10580496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10590496c8aeSRafael J. Wysocki * devices. 10600496c8aeSRafael J. Wysocki */ 10610496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 10620496c8aeSRafael J. Wysocki { 10630496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10640496c8aeSRafael J. Wysocki 10650496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10660496c8aeSRafael J. Wysocki 10670496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10680496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10690496c8aeSRafael J. Wysocki return -EINVAL; 10700496c8aeSRafael J. Wysocki 10710496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); 10720496c8aeSRafael J. Wysocki } 10730496c8aeSRafael J. Wysocki 10740496c8aeSRafael J. Wysocki /** 10750496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1076596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1077596ba34bSRafael J. Wysocki * 1078596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1079596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1080596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1081596ba34bSRafael J. Wysocki * devices. 1082596ba34bSRafael J. Wysocki */ 1083596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1084596ba34bSRafael J. Wysocki { 1085596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1086596ba34bSRafael J. Wysocki 1087596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1088596ba34bSRafael J. Wysocki 1089596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1090596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1091596ba34bSRafael J. Wysocki return -EINVAL; 1092596ba34bSRafael J. Wysocki 10931e78a0c7SRafael J. Wysocki return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? 10941e78a0c7SRafael J. Wysocki 0 : genpd_stop_dev(genpd, dev); 1095596ba34bSRafael J. Wysocki } 1096596ba34bSRafael J. Wysocki 1097596ba34bSRafael J. Wysocki /** 10980496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1099596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1100596ba34bSRafael J. Wysocki * 11010496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 11020496c8aeSRafael J. Wysocki * before the system transition. 1103596ba34bSRafael J. Wysocki */ 1104596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1105596ba34bSRafael J. Wysocki { 1106596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1107596ba34bSRafael J. Wysocki 1108596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1109596ba34bSRafael J. Wysocki 1110596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1111596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1112596ba34bSRafael J. Wysocki return -EINVAL; 1113596ba34bSRafael J. Wysocki 11141e78a0c7SRafael J. Wysocki return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? 11151e78a0c7SRafael J. Wysocki 0 : genpd_start_dev(genpd, dev); 11160496c8aeSRafael J. Wysocki } 1117596ba34bSRafael J. Wysocki 11180496c8aeSRafael J. Wysocki /** 11190496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 11200496c8aeSRafael J. Wysocki * @dev: Device to thaw. 11210496c8aeSRafael J. Wysocki * 11220496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 11230496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 11240496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 11250496c8aeSRafael J. Wysocki * devices. 11260496c8aeSRafael J. Wysocki */ 11270496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 11280496c8aeSRafael J. Wysocki { 11290496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1130596ba34bSRafael J. Wysocki 11310496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 11320496c8aeSRafael J. Wysocki 11330496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11340496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11350496c8aeSRafael J. Wysocki return -EINVAL; 11360496c8aeSRafael J. Wysocki 11370496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); 1138596ba34bSRafael J. Wysocki } 1139596ba34bSRafael J. Wysocki 1140596ba34bSRafael J. Wysocki /** 1141596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1142596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1143596ba34bSRafael J. Wysocki * 1144596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1145596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1146596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1147596ba34bSRafael J. Wysocki */ 1148596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1149596ba34bSRafael J. Wysocki { 1150596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1151596ba34bSRafael J. Wysocki 1152596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1153596ba34bSRafael J. Wysocki 1154596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1155596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1156596ba34bSRafael J. Wysocki return -EINVAL; 1157596ba34bSRafael J. Wysocki 1158d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); 1159596ba34bSRafael J. Wysocki } 1160596ba34bSRafael J. Wysocki 1161596ba34bSRafael J. Wysocki /** 11620496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1163596ba34bSRafael J. Wysocki * @dev: Device to resume. 1164596ba34bSRafael J. Wysocki * 11650496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 11660496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1167596ba34bSRafael J. Wysocki */ 1168596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1169596ba34bSRafael J. Wysocki { 1170596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1171596ba34bSRafael J. Wysocki 1172596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1173596ba34bSRafael J. Wysocki 1174596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1175596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1176596ba34bSRafael J. Wysocki return -EINVAL; 1177596ba34bSRafael J. Wysocki 1178596ba34bSRafael J. Wysocki /* 1179596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1180596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1181596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 118265533bbfSRafael J. Wysocki * 118365533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 118465533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 118565533bbfSRafael J. Wysocki */ 118665533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 118765533bbfSRafael J. Wysocki /* 118865533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 118965533bbfSRafael J. Wysocki * so make it appear as powered off to pm_genpd_poweron(), so 119065533bbfSRafael J. Wysocki * that it tries to power it on in case it was really off. 1191596ba34bSRafael J. Wysocki */ 119217b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1193596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1194596ba34bSRafael J. Wysocki /* 119565533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 119665533bbfSRafael J. Wysocki * sure it will be off going forward. 1197596ba34bSRafael J. Wysocki */ 1198596ba34bSRafael J. Wysocki if (genpd->power_off) 1199596ba34bSRafael J. Wysocki genpd->power_off(genpd); 120065533bbfSRafael J. Wysocki 1201596ba34bSRafael J. Wysocki return 0; 1202596ba34bSRafael J. Wysocki } 120365533bbfSRafael J. Wysocki } 1204596ba34bSRafael J. Wysocki 120518dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 120618dd2eceSRafael J. Wysocki return 0; 120718dd2eceSRafael J. Wysocki 1208596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 1209596ba34bSRafael J. Wysocki 12101e78a0c7SRafael J. Wysocki return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev); 1211596ba34bSRafael J. Wysocki } 1212596ba34bSRafael J. Wysocki 1213596ba34bSRafael J. Wysocki /** 1214596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1215596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1216596ba34bSRafael J. Wysocki * 1217596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1218596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1219596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1220596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1221596ba34bSRafael J. Wysocki */ 1222596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1223596ba34bSRafael J. Wysocki { 1224596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1225596ba34bSRafael J. Wysocki bool run_complete; 1226596ba34bSRafael J. Wysocki 1227596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1228596ba34bSRafael J. Wysocki 1229596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1230596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1231596ba34bSRafael J. Wysocki return; 1232596ba34bSRafael J. Wysocki 1233596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1234596ba34bSRafael J. Wysocki 1235596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1236596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1237596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1238596ba34bSRafael J. Wysocki 1239596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1240596ba34bSRafael J. Wysocki 1241596ba34bSRafael J. Wysocki if (run_complete) { 1242596ba34bSRafael J. Wysocki pm_generic_complete(dev); 12436f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1244596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 12456f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 1246596ba34bSRafael J. Wysocki } 1247596ba34bSRafael J. Wysocki } 1248596ba34bSRafael J. Wysocki 1249596ba34bSRafael J. Wysocki #else 1250596ba34bSRafael J. Wysocki 1251596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1252596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 12530496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1254596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 12550496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1256596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1257596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1258596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 12590496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1260596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 12610496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1262596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1263596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1264596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1265596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1266596ba34bSRafael J. Wysocki 1267596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1268596ba34bSRafael J. Wysocki 1269f721889fSRafael J. Wysocki /** 1270b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1271f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1272f721889fSRafael J. Wysocki * @dev: Device to be added. 1273b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1274f721889fSRafael J. Wysocki */ 1275b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1276b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1277f721889fSRafael J. Wysocki { 1278cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12794605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1280f721889fSRafael J. Wysocki int ret = 0; 1281f721889fSRafael J. Wysocki 1282f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1283f721889fSRafael J. Wysocki 1284f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1285f721889fSRafael J. Wysocki return -EINVAL; 1286f721889fSRafael J. Wysocki 12876ff7bb0dSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 12886ff7bb0dSRafael J. Wysocki if (!gpd_data) 12896ff7bb0dSRafael J. Wysocki return -ENOMEM; 12906ff7bb0dSRafael J. Wysocki 12916ff7bb0dSRafael J. Wysocki mutex_init(&gpd_data->lock); 12926ff7bb0dSRafael J. Wysocki gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 12936ff7bb0dSRafael J. Wysocki dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12946ff7bb0dSRafael J. Wysocki 129517b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1296f721889fSRafael J. Wysocki 1297596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1298596ba34bSRafael J. Wysocki ret = -EAGAIN; 1299596ba34bSRafael J. Wysocki goto out; 1300596ba34bSRafael J. Wysocki } 1301596ba34bSRafael J. Wysocki 13024605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 13034605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1304f721889fSRafael J. Wysocki ret = -EINVAL; 1305f721889fSRafael J. Wysocki goto out; 1306f721889fSRafael J. Wysocki } 1307f721889fSRafael J. Wysocki 1308596ba34bSRafael J. Wysocki genpd->device_count++; 13096ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1310f721889fSRafael J. Wysocki 13114605ab65SRafael J. Wysocki dev_pm_get_subsys_data(dev); 13126ff7bb0dSRafael J. Wysocki 13136ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 13146ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 13156ff7bb0dSRafael J. Wysocki dev->pm_domain = &genpd->domain; 1316cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 1317cd0ea672SRafael J. Wysocki gpd_data->base.dev = dev; 1318cd0ea672SRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 1319ca1d72f0SRafael J. Wysocki gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 1320b02c999aSRafael J. Wysocki if (td) 1321b02c999aSRafael J. Wysocki gpd_data->td = *td; 1322f721889fSRafael J. Wysocki 13236ff7bb0dSRafael J. Wysocki gpd_data->td.constraint_changed = true; 13246ff7bb0dSRafael J. Wysocki gpd_data->td.effective_constraint_ns = -1; 13256ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 13266ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 13276ff7bb0dSRafael J. Wysocki 13286ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 13296ff7bb0dSRafael J. Wysocki 13306ff7bb0dSRafael J. Wysocki return 0; 13316ff7bb0dSRafael J. Wysocki 1332f721889fSRafael J. Wysocki out: 133317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1334f721889fSRafael J. Wysocki 13356ff7bb0dSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13366ff7bb0dSRafael J. Wysocki kfree(gpd_data); 1337f721889fSRafael J. Wysocki return ret; 1338f721889fSRafael J. Wysocki } 1339f721889fSRafael J. Wysocki 1340f721889fSRafael J. Wysocki /** 1341c8aa130bSThomas Abraham * __pm_genpd_of_add_device - Add a device to an I/O PM domain. 1342c8aa130bSThomas Abraham * @genpd_node: Device tree node pointer representing a PM domain to which the 1343c8aa130bSThomas Abraham * the device is added to. 1344c8aa130bSThomas Abraham * @dev: Device to be added. 1345c8aa130bSThomas Abraham * @td: Set of PM QoS timing parameters to attach to the device. 1346c8aa130bSThomas Abraham */ 1347c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, 1348c8aa130bSThomas Abraham struct gpd_timing_data *td) 1349c8aa130bSThomas Abraham { 1350c8aa130bSThomas Abraham struct generic_pm_domain *genpd = NULL, *gpd; 1351c8aa130bSThomas Abraham 1352c8aa130bSThomas Abraham dev_dbg(dev, "%s()\n", __func__); 1353c8aa130bSThomas Abraham 1354c8aa130bSThomas Abraham if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) 1355c8aa130bSThomas Abraham return -EINVAL; 1356c8aa130bSThomas Abraham 1357c8aa130bSThomas Abraham mutex_lock(&gpd_list_lock); 1358c8aa130bSThomas Abraham list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1359c8aa130bSThomas Abraham if (gpd->of_node == genpd_node) { 1360c8aa130bSThomas Abraham genpd = gpd; 1361c8aa130bSThomas Abraham break; 1362c8aa130bSThomas Abraham } 1363c8aa130bSThomas Abraham } 1364c8aa130bSThomas Abraham mutex_unlock(&gpd_list_lock); 1365c8aa130bSThomas Abraham 1366c8aa130bSThomas Abraham if (!genpd) 1367c8aa130bSThomas Abraham return -EINVAL; 1368c8aa130bSThomas Abraham 1369c8aa130bSThomas Abraham return __pm_genpd_add_device(genpd, dev, td); 1370c8aa130bSThomas Abraham } 1371c8aa130bSThomas Abraham 1372c8aa130bSThomas Abraham /** 1373f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1374f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1375f721889fSRafael J. Wysocki * @dev: Device to be removed. 1376f721889fSRafael J. Wysocki */ 1377f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1378f721889fSRafael J. Wysocki struct device *dev) 1379f721889fSRafael J. Wysocki { 13806ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13814605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1382efa69025SRafael J. Wysocki int ret = 0; 1383f721889fSRafael J. Wysocki 1384f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1385f721889fSRafael J. Wysocki 1386efa69025SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) 1387efa69025SRafael J. Wysocki || IS_ERR_OR_NULL(dev->pm_domain) 1388efa69025SRafael J. Wysocki || pd_to_genpd(dev->pm_domain) != genpd) 1389f721889fSRafael J. Wysocki return -EINVAL; 1390f721889fSRafael J. Wysocki 139117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1392f721889fSRafael J. Wysocki 1393596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1394596ba34bSRafael J. Wysocki ret = -EAGAIN; 1395596ba34bSRafael J. Wysocki goto out; 1396596ba34bSRafael J. Wysocki } 1397596ba34bSRafael J. Wysocki 13986ff7bb0dSRafael J. Wysocki genpd->device_count--; 13996ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 14006ff7bb0dSRafael J. Wysocki 14016ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1402f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1403efa69025SRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1404efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 1405efa69025SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 14066ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1407f721889fSRafael J. Wysocki 14086ff7bb0dSRafael J. Wysocki gpd_data = to_gpd_data(pdd); 14096ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 14106ff7bb0dSRafael J. Wysocki pdd->dev = NULL; 14116ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 14126ff7bb0dSRafael J. Wysocki 14136ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 14146ff7bb0dSRafael J. Wysocki 14156ff7bb0dSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 14166ff7bb0dSRafael J. Wysocki kfree(gpd_data); 14176ff7bb0dSRafael J. Wysocki dev_pm_put_subsys_data(dev); 14186ff7bb0dSRafael J. Wysocki return 0; 1419f721889fSRafael J. Wysocki 1420596ba34bSRafael J. Wysocki out: 142117b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1422f721889fSRafael J. Wysocki 1423f721889fSRafael J. Wysocki return ret; 1424f721889fSRafael J. Wysocki } 1425f721889fSRafael J. Wysocki 1426f721889fSRafael J. Wysocki /** 14271e78a0c7SRafael J. Wysocki * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device. 14281e78a0c7SRafael J. Wysocki * @dev: Device to set/unset the flag for. 14291e78a0c7SRafael J. Wysocki * @val: The new value of the device's "always on" flag. 14301e78a0c7SRafael J. Wysocki */ 14311e78a0c7SRafael J. Wysocki void pm_genpd_dev_always_on(struct device *dev, bool val) 14321e78a0c7SRafael J. Wysocki { 14331e78a0c7SRafael J. Wysocki struct pm_subsys_data *psd; 14341e78a0c7SRafael J. Wysocki unsigned long flags; 14351e78a0c7SRafael J. Wysocki 14361e78a0c7SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 14371e78a0c7SRafael J. Wysocki 14381e78a0c7SRafael J. Wysocki psd = dev_to_psd(dev); 14391e78a0c7SRafael J. Wysocki if (psd && psd->domain_data) 14401e78a0c7SRafael J. Wysocki to_gpd_data(psd->domain_data)->always_on = val; 14411e78a0c7SRafael J. Wysocki 14421e78a0c7SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 14431e78a0c7SRafael J. Wysocki } 14441e78a0c7SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); 14451e78a0c7SRafael J. Wysocki 14461e78a0c7SRafael J. Wysocki /** 1447ca1d72f0SRafael J. Wysocki * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. 1448ca1d72f0SRafael J. Wysocki * @dev: Device to set/unset the flag for. 1449ca1d72f0SRafael J. Wysocki * @val: The new value of the device's "need restore" flag. 1450ca1d72f0SRafael J. Wysocki */ 1451ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val) 1452ca1d72f0SRafael J. Wysocki { 1453ca1d72f0SRafael J. Wysocki struct pm_subsys_data *psd; 1454ca1d72f0SRafael J. Wysocki unsigned long flags; 1455ca1d72f0SRafael J. Wysocki 1456ca1d72f0SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 1457ca1d72f0SRafael J. Wysocki 1458ca1d72f0SRafael J. Wysocki psd = dev_to_psd(dev); 1459ca1d72f0SRafael J. Wysocki if (psd && psd->domain_data) 1460ca1d72f0SRafael J. Wysocki to_gpd_data(psd->domain_data)->need_restore = val; 1461ca1d72f0SRafael J. Wysocki 1462ca1d72f0SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 1463ca1d72f0SRafael J. Wysocki } 1464ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); 1465ca1d72f0SRafael J. Wysocki 1466ca1d72f0SRafael J. Wysocki /** 1467f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1468f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1469bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1470f721889fSRafael J. Wysocki */ 1471f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1472bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1473f721889fSRafael J. Wysocki { 14745063ce15SRafael J. Wysocki struct gpd_link *link; 1475f721889fSRafael J. Wysocki int ret = 0; 1476f721889fSRafael J. Wysocki 1477bc0403ffSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1478f721889fSRafael J. Wysocki return -EINVAL; 1479f721889fSRafael J. Wysocki 148017b75ecaSRafael J. Wysocki start: 148117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1482bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1483f721889fSRafael J. Wysocki 1484bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1485bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1486bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 148717b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 148817b75ecaSRafael J. Wysocki goto start; 148917b75ecaSRafael J. Wysocki } 149017b75ecaSRafael J. Wysocki 149117b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1492bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1493f721889fSRafael J. Wysocki ret = -EINVAL; 1494f721889fSRafael J. Wysocki goto out; 1495f721889fSRafael J. Wysocki } 1496f721889fSRafael J. Wysocki 14974fcac10dSHuang Ying list_for_each_entry(link, &genpd->master_links, master_node) { 1498bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1499f721889fSRafael J. Wysocki ret = -EINVAL; 1500f721889fSRafael J. Wysocki goto out; 1501f721889fSRafael J. Wysocki } 1502f721889fSRafael J. Wysocki } 1503f721889fSRafael J. Wysocki 15045063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 15055063ce15SRafael J. Wysocki if (!link) { 15065063ce15SRafael J. Wysocki ret = -ENOMEM; 15075063ce15SRafael J. Wysocki goto out; 15085063ce15SRafael J. Wysocki } 15095063ce15SRafael J. Wysocki link->master = genpd; 15105063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1511bc0403ffSRafael J. Wysocki link->slave = subdomain; 1512bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1513bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1514c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1515f721889fSRafael J. Wysocki 1516f721889fSRafael J. Wysocki out: 1517bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 151817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1519f721889fSRafael J. Wysocki 1520f721889fSRafael J. Wysocki return ret; 1521f721889fSRafael J. Wysocki } 1522f721889fSRafael J. Wysocki 1523f721889fSRafael J. Wysocki /** 1524f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1525f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 15265063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1527f721889fSRafael J. Wysocki */ 1528f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 15295063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1530f721889fSRafael J. Wysocki { 15315063ce15SRafael J. Wysocki struct gpd_link *link; 1532f721889fSRafael J. Wysocki int ret = -EINVAL; 1533f721889fSRafael J. Wysocki 15345063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1535f721889fSRafael J. Wysocki return -EINVAL; 1536f721889fSRafael J. Wysocki 153717b75ecaSRafael J. Wysocki start: 153817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1539f721889fSRafael J. Wysocki 15405063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 15415063ce15SRafael J. Wysocki if (link->slave != subdomain) 1542f721889fSRafael J. Wysocki continue; 1543f721889fSRafael J. Wysocki 1544f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1545f721889fSRafael J. Wysocki 154617b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 154717b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 154817b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 154917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 155017b75ecaSRafael J. Wysocki goto start; 155117b75ecaSRafael J. Wysocki } 155217b75ecaSRafael J. Wysocki 15535063ce15SRafael J. Wysocki list_del(&link->master_node); 15545063ce15SRafael J. Wysocki list_del(&link->slave_node); 15555063ce15SRafael J. Wysocki kfree(link); 155617b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1557f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1558f721889fSRafael J. Wysocki 1559f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1560f721889fSRafael J. Wysocki 1561f721889fSRafael J. Wysocki ret = 0; 1562f721889fSRafael J. Wysocki break; 1563f721889fSRafael J. Wysocki } 1564f721889fSRafael J. Wysocki 156517b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1566f721889fSRafael J. Wysocki 1567f721889fSRafael J. Wysocki return ret; 1568f721889fSRafael J. Wysocki } 1569f721889fSRafael J. Wysocki 1570f721889fSRafael J. Wysocki /** 1571d5e4cbfeSRafael J. Wysocki * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. 1572d5e4cbfeSRafael J. Wysocki * @dev: Device to add the callbacks to. 1573d5e4cbfeSRafael J. Wysocki * @ops: Set of callbacks to add. 1574b02c999aSRafael J. Wysocki * @td: Timing data to add to the device along with the callbacks (optional). 1575d5e4cbfeSRafael J. Wysocki */ 1576b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, 1577b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1578d5e4cbfeSRafael J. Wysocki { 1579d5e4cbfeSRafael J. Wysocki struct pm_domain_data *pdd; 1580d5e4cbfeSRafael J. Wysocki int ret = 0; 1581d5e4cbfeSRafael J. Wysocki 1582d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data && ops)) 1583d5e4cbfeSRafael J. Wysocki return -EINVAL; 1584d5e4cbfeSRafael J. Wysocki 1585d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1586d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1587d5e4cbfeSRafael J. Wysocki 1588d5e4cbfeSRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1589d5e4cbfeSRafael J. Wysocki if (pdd) { 1590d5e4cbfeSRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1591d5e4cbfeSRafael J. Wysocki 1592d5e4cbfeSRafael J. Wysocki gpd_data->ops = *ops; 1593b02c999aSRafael J. Wysocki if (td) 1594b02c999aSRafael J. Wysocki gpd_data->td = *td; 1595d5e4cbfeSRafael J. Wysocki } else { 1596d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1597d5e4cbfeSRafael J. Wysocki } 1598d5e4cbfeSRafael J. Wysocki 1599d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1600d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1601d5e4cbfeSRafael J. Wysocki 1602d5e4cbfeSRafael J. Wysocki return ret; 1603d5e4cbfeSRafael J. Wysocki } 1604d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); 1605d5e4cbfeSRafael J. Wysocki 1606d5e4cbfeSRafael J. Wysocki /** 1607b02c999aSRafael J. Wysocki * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. 1608d5e4cbfeSRafael J. Wysocki * @dev: Device to remove the callbacks from. 1609b02c999aSRafael J. Wysocki * @clear_td: If set, clear the device's timing data too. 1610d5e4cbfeSRafael J. Wysocki */ 1611b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) 1612d5e4cbfeSRafael J. Wysocki { 1613d5e4cbfeSRafael J. Wysocki struct pm_domain_data *pdd; 1614d5e4cbfeSRafael J. Wysocki int ret = 0; 1615d5e4cbfeSRafael J. Wysocki 1616d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data)) 1617d5e4cbfeSRafael J. Wysocki return -EINVAL; 1618d5e4cbfeSRafael J. Wysocki 1619d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1620d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1621d5e4cbfeSRafael J. Wysocki 1622d5e4cbfeSRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1623d5e4cbfeSRafael J. Wysocki if (pdd) { 1624d5e4cbfeSRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1625d5e4cbfeSRafael J. Wysocki 1626d5e4cbfeSRafael J. Wysocki gpd_data->ops = (struct gpd_dev_ops){ 0 }; 1627b02c999aSRafael J. Wysocki if (clear_td) 1628b02c999aSRafael J. Wysocki gpd_data->td = (struct gpd_timing_data){ 0 }; 1629d5e4cbfeSRafael J. Wysocki } else { 1630d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1631d5e4cbfeSRafael J. Wysocki } 1632d5e4cbfeSRafael J. Wysocki 1633d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1634d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1635d5e4cbfeSRafael J. Wysocki 1636d5e4cbfeSRafael J. Wysocki return ret; 1637d5e4cbfeSRafael J. Wysocki } 1638b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); 1639d5e4cbfeSRafael J. Wysocki 1640cbc9ef02SRafael J. Wysocki int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) 1641cbc9ef02SRafael J. Wysocki { 1642cbc9ef02SRafael J. Wysocki struct cpuidle_driver *cpuidle_drv; 1643cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1644cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1645cbc9ef02SRafael J. Wysocki int ret = 0; 1646cbc9ef02SRafael J. Wysocki 1647cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || state < 0) 1648cbc9ef02SRafael J. Wysocki return -EINVAL; 1649cbc9ef02SRafael J. Wysocki 1650cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1651cbc9ef02SRafael J. Wysocki 1652cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 1653cbc9ef02SRafael J. Wysocki ret = -EEXIST; 1654cbc9ef02SRafael J. Wysocki goto out; 1655cbc9ef02SRafael J. Wysocki } 1656cbc9ef02SRafael J. Wysocki cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); 1657cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1658cbc9ef02SRafael J. Wysocki ret = -ENOMEM; 1659cbc9ef02SRafael J. Wysocki goto out; 1660cbc9ef02SRafael J. Wysocki } 1661cbc9ef02SRafael J. Wysocki cpuidle_drv = cpuidle_driver_ref(); 1662cbc9ef02SRafael J. Wysocki if (!cpuidle_drv) { 1663cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1664cbc9ef02SRafael J. Wysocki goto out; 1665cbc9ef02SRafael J. Wysocki } 1666cbc9ef02SRafael J. Wysocki if (cpuidle_drv->state_count <= state) { 1667cbc9ef02SRafael J. Wysocki ret = -EINVAL; 1668cbc9ef02SRafael J. Wysocki goto err; 1669cbc9ef02SRafael J. Wysocki } 1670cbc9ef02SRafael J. Wysocki idle_state = &cpuidle_drv->states[state]; 1671cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1672cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1673cbc9ef02SRafael J. Wysocki goto err; 1674cbc9ef02SRafael J. Wysocki } 1675cbc9ef02SRafael J. Wysocki cpu_data->idle_state = idle_state; 1676cbc9ef02SRafael J. Wysocki cpu_data->saved_exit_latency = idle_state->exit_latency; 1677cbc9ef02SRafael J. Wysocki genpd->cpu_data = cpu_data; 1678cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 1679cbc9ef02SRafael J. Wysocki 1680cbc9ef02SRafael J. Wysocki out: 1681cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1682cbc9ef02SRafael J. Wysocki return ret; 1683cbc9ef02SRafael J. Wysocki 1684cbc9ef02SRafael J. Wysocki err: 1685cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1686cbc9ef02SRafael J. Wysocki goto out; 1687cbc9ef02SRafael J. Wysocki } 1688cbc9ef02SRafael J. Wysocki 1689cbc9ef02SRafael J. Wysocki int genpd_detach_cpuidle(struct generic_pm_domain *genpd) 1690cbc9ef02SRafael J. Wysocki { 1691cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1692cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1693cbc9ef02SRafael J. Wysocki int ret = 0; 1694cbc9ef02SRafael J. Wysocki 1695cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1696cbc9ef02SRafael J. Wysocki return -EINVAL; 1697cbc9ef02SRafael J. Wysocki 1698cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1699cbc9ef02SRafael J. Wysocki 1700cbc9ef02SRafael J. Wysocki cpu_data = genpd->cpu_data; 1701cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1702cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1703cbc9ef02SRafael J. Wysocki goto out; 1704cbc9ef02SRafael J. Wysocki } 1705cbc9ef02SRafael J. Wysocki idle_state = cpu_data->idle_state; 1706cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1707cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1708cbc9ef02SRafael J. Wysocki goto out; 1709cbc9ef02SRafael J. Wysocki } 1710cbc9ef02SRafael J. Wysocki idle_state->exit_latency = cpu_data->saved_exit_latency; 1711cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1712cbc9ef02SRafael J. Wysocki genpd->cpu_data = NULL; 1713cbc9ef02SRafael J. Wysocki kfree(cpu_data); 1714cbc9ef02SRafael J. Wysocki 1715cbc9ef02SRafael J. Wysocki out: 1716cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1717cbc9ef02SRafael J. Wysocki return ret; 1718cbc9ef02SRafael J. Wysocki } 1719cbc9ef02SRafael J. Wysocki 1720d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1721d23b9b00SRafael J. Wysocki 1722d5e4cbfeSRafael J. Wysocki /** 1723ecf00475SRafael J. Wysocki * pm_genpd_default_save_state - Default "save device state" for PM domians. 1724ecf00475SRafael J. Wysocki * @dev: Device to handle. 1725ecf00475SRafael J. Wysocki */ 1726ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1727ecf00475SRafael J. Wysocki { 1728ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1729ecf00475SRafael J. Wysocki 1730ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.save_state; 1731ecf00475SRafael J. Wysocki if (cb) 1732ecf00475SRafael J. Wysocki return cb(dev); 1733ecf00475SRafael J. Wysocki 17340b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 17350b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 17360b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 17370b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 17380b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 17390b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 17400b589741SRafael J. Wysocki else 17410b589741SRafael J. Wysocki cb = NULL; 1742ecf00475SRafael J. Wysocki 17430b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 17440b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 17450b589741SRafael J. Wysocki 17460b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1747ecf00475SRafael J. Wysocki } 1748ecf00475SRafael J. Wysocki 1749ecf00475SRafael J. Wysocki /** 1750ecf00475SRafael J. Wysocki * pm_genpd_default_restore_state - Default PM domians "restore device state". 1751ecf00475SRafael J. Wysocki * @dev: Device to handle. 1752ecf00475SRafael J. Wysocki */ 1753ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1754ecf00475SRafael J. Wysocki { 1755ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1756ecf00475SRafael J. Wysocki 1757ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.restore_state; 1758ecf00475SRafael J. Wysocki if (cb) 1759ecf00475SRafael J. Wysocki return cb(dev); 1760ecf00475SRafael J. Wysocki 17610b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 17620b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 17630b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 17640b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 17650b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 17660b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 17670b589741SRafael J. Wysocki else 17680b589741SRafael J. Wysocki cb = NULL; 1769ecf00475SRafael J. Wysocki 17700b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 17710b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 17720b589741SRafael J. Wysocki 17730b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1774ecf00475SRafael J. Wysocki } 1775ecf00475SRafael J. Wysocki 17760f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 17770f1d6986SRafael J. Wysocki 1778ecf00475SRafael J. Wysocki /** 1779d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend - Default "device suspend" for PM domians. 1780d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1781d23b9b00SRafael J. Wysocki */ 1782d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev) 1783d23b9b00SRafael J. Wysocki { 1784c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend; 1785d23b9b00SRafael J. Wysocki 1786d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend(dev); 1787d23b9b00SRafael J. Wysocki } 1788d23b9b00SRafael J. Wysocki 1789d23b9b00SRafael J. Wysocki /** 1790d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. 1791d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1792d23b9b00SRafael J. Wysocki */ 1793d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev) 1794d23b9b00SRafael J. Wysocki { 1795c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; 1796d23b9b00SRafael J. Wysocki 17970496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend_late(dev); 1798d23b9b00SRafael J. Wysocki } 1799d23b9b00SRafael J. Wysocki 1800d23b9b00SRafael J. Wysocki /** 1801d23b9b00SRafael J. Wysocki * pm_genpd_default_resume_early - Default "early device resume" for PM domians. 1802d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1803d23b9b00SRafael J. Wysocki */ 1804d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev) 1805d23b9b00SRafael J. Wysocki { 1806c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; 1807d23b9b00SRafael J. Wysocki 18080496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume_early(dev); 1809d23b9b00SRafael J. Wysocki } 1810d23b9b00SRafael J. Wysocki 1811d23b9b00SRafael J. Wysocki /** 1812d23b9b00SRafael J. Wysocki * pm_genpd_default_resume - Default "device resume" for PM domians. 1813d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1814d23b9b00SRafael J. Wysocki */ 1815d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev) 1816d23b9b00SRafael J. Wysocki { 1817c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume; 1818d23b9b00SRafael J. Wysocki 1819d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume(dev); 1820d23b9b00SRafael J. Wysocki } 1821d23b9b00SRafael J. Wysocki 1822d23b9b00SRafael J. Wysocki /** 1823d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze - Default "device freeze" for PM domians. 1824d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1825d23b9b00SRafael J. Wysocki */ 1826d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev) 1827d23b9b00SRafael J. Wysocki { 1828d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; 1829d23b9b00SRafael J. Wysocki 1830d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze(dev); 1831d23b9b00SRafael J. Wysocki } 1832d23b9b00SRafael J. Wysocki 1833d23b9b00SRafael J. Wysocki /** 1834d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. 1835d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1836d23b9b00SRafael J. Wysocki */ 1837d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev) 1838d23b9b00SRafael J. Wysocki { 1839d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; 1840d23b9b00SRafael J. Wysocki 18410496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze_late(dev); 1842d23b9b00SRafael J. Wysocki } 1843d23b9b00SRafael J. Wysocki 1844d23b9b00SRafael J. Wysocki /** 1845d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. 1846d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1847d23b9b00SRafael J. Wysocki */ 1848d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev) 1849d23b9b00SRafael J. Wysocki { 1850d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; 1851d23b9b00SRafael J. Wysocki 18520496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw_early(dev); 1853d23b9b00SRafael J. Wysocki } 1854d23b9b00SRafael J. Wysocki 1855d23b9b00SRafael J. Wysocki /** 1856d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw - Default "device thaw" for PM domians. 1857d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1858d23b9b00SRafael J. Wysocki */ 1859d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev) 1860d23b9b00SRafael J. Wysocki { 1861d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; 1862d23b9b00SRafael J. Wysocki 1863d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw(dev); 1864d23b9b00SRafael J. Wysocki } 1865d23b9b00SRafael J. Wysocki 18660f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 18670f1d6986SRafael J. Wysocki 18680f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend NULL 18690f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late NULL 18700f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early NULL 18710f1d6986SRafael J. Wysocki #define pm_genpd_default_resume NULL 18720f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze NULL 18730f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late NULL 18740f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early NULL 18750f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw NULL 18760f1d6986SRafael J. Wysocki 18770f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */ 18780f1d6986SRafael J. Wysocki 1879d23b9b00SRafael J. Wysocki /** 1880f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1881f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1882f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1883f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1884f721889fSRafael J. Wysocki */ 1885f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1886f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1887f721889fSRafael J. Wysocki { 1888f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1889f721889fSRafael J. Wysocki return; 1890f721889fSRafael J. Wysocki 18915063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 18925063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1893f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1894f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1895f721889fSRafael J. Wysocki genpd->gov = gov; 1896f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1897f721889fSRafael J. Wysocki genpd->in_progress = 0; 1898c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 189917b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 190017b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 1901c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 1902c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 1903596ba34bSRafael J. Wysocki genpd->device_count = 0; 1904221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 19056ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1906f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1907f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1908f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1909596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1910596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 19110496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 1912596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1913596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 19140496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 1915596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1916596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 19170496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 1918596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1919596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 19200496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 1921596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1922d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 19230496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 1924d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1925596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 19260496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 1927d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 1928596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1929ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 1930ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 1931c9914854SRafael J. Wysocki genpd->dev_ops.suspend = pm_genpd_default_suspend; 1932c9914854SRafael J. Wysocki genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late; 1933c9914854SRafael J. Wysocki genpd->dev_ops.resume_early = pm_genpd_default_resume_early; 1934c9914854SRafael J. Wysocki genpd->dev_ops.resume = pm_genpd_default_resume; 1935d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze = pm_genpd_default_freeze; 1936d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; 1937d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; 1938d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw = pm_genpd_default_thaw; 19395125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 19405125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 19415125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 19425125bbf3SRafael J. Wysocki } 1943