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 142f721889fSRafael J. Wysocki /** 1435063ce15SRafael J. Wysocki * __pm_genpd_poweron - Restore power to a given PM domain and its masters. 1445248051bSRafael J. Wysocki * @genpd: PM domain to power up. 1455248051bSRafael J. Wysocki * 1465063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 1475248051bSRafael J. Wysocki * resume a device belonging to it. 1485248051bSRafael J. Wysocki */ 1493f241775SRafael J. Wysocki int __pm_genpd_poweron(struct generic_pm_domain *genpd) 1503f241775SRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 1515248051bSRafael J. Wysocki { 1525063ce15SRafael J. Wysocki struct gpd_link *link; 1533f241775SRafael J. Wysocki DEFINE_WAIT(wait); 1545248051bSRafael J. Wysocki int ret = 0; 1555248051bSRafael J. Wysocki 1565063ce15SRafael J. Wysocki /* If the domain's master is being waited for, we have to wait too. */ 1573f241775SRafael J. Wysocki for (;;) { 1583f241775SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 1593f241775SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 16017877eb5SRafael J. Wysocki if (genpd->status != GPD_STATE_WAIT_MASTER) 1613f241775SRafael J. Wysocki break; 1623f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 1633f241775SRafael J. Wysocki 1643f241775SRafael J. Wysocki schedule(); 1653f241775SRafael J. Wysocki 16617b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 1673f241775SRafael J. Wysocki } 1683f241775SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 16917b75ecaSRafael J. Wysocki 17017b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 171596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1723f241775SRafael J. Wysocki return 0; 1735248051bSRafael J. Wysocki 174c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 175c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 1763f241775SRafael J. Wysocki return 0; 177c6d22b37SRafael J. Wysocki } 178c6d22b37SRafael J. Wysocki 1795063ce15SRafael J. Wysocki /* 1805063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 1815063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 1825063ce15SRafael J. Wysocki * with it. 1835063ce15SRafael J. Wysocki */ 1845063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1855063ce15SRafael J. Wysocki genpd_sd_counter_inc(link->master); 18617877eb5SRafael J. Wysocki genpd->status = GPD_STATE_WAIT_MASTER; 1873c07cbc4SRafael J. Wysocki 1885248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1895248051bSRafael J. Wysocki 1905063ce15SRafael J. Wysocki ret = pm_genpd_poweron(link->master); 1919e08cf42SRafael J. Wysocki 1929e08cf42SRafael J. Wysocki mutex_lock(&genpd->lock); 1939e08cf42SRafael J. Wysocki 1943f241775SRafael J. Wysocki /* 1953f241775SRafael J. Wysocki * The "wait for parent" status is guaranteed not to change 1965063ce15SRafael J. Wysocki * while the master is powering on. 1973f241775SRafael J. Wysocki */ 1983f241775SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1993f241775SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 2005063ce15SRafael J. Wysocki if (ret) { 2015063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2029e08cf42SRafael J. Wysocki goto err; 2035248051bSRafael J. Wysocki } 2045063ce15SRafael J. Wysocki } 2055248051bSRafael J. Wysocki 2069e08cf42SRafael J. Wysocki if (genpd->power_on) { 2070140d8bdSRafael J. Wysocki ktime_t time_start = ktime_get(); 2080140d8bdSRafael J. Wysocki s64 elapsed_ns; 2090140d8bdSRafael J. Wysocki 210fe202fdeSRafael J. Wysocki ret = genpd->power_on(genpd); 2119e08cf42SRafael J. Wysocki if (ret) 2129e08cf42SRafael J. Wysocki goto err; 2130140d8bdSRafael J. Wysocki 2140140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 215e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_on_latency_ns) { 2160140d8bdSRafael J. Wysocki genpd->power_on_latency_ns = elapsed_ns; 2176ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 218e84b2c20SRafael J. Wysocki if (genpd->name) 219e84b2c20SRafael J. Wysocki pr_warning("%s: Power-on latency exceeded, " 220e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 221e84b2c20SRafael J. Wysocki elapsed_ns); 222e84b2c20SRafael J. Wysocki } 2233c07cbc4SRafael J. Wysocki } 2245248051bSRafael J. Wysocki 2259e08cf42SRafael J. Wysocki genpd_set_active(genpd); 2269e08cf42SRafael J. Wysocki 2273f241775SRafael J. Wysocki return 0; 2289e08cf42SRafael J. Wysocki 2299e08cf42SRafael J. Wysocki err: 2305063ce15SRafael J. Wysocki list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) 2315063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2329e08cf42SRafael J. Wysocki 2333f241775SRafael J. Wysocki return ret; 2343f241775SRafael J. Wysocki } 2353f241775SRafael J. Wysocki 2363f241775SRafael J. Wysocki /** 2375063ce15SRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its masters. 2383f241775SRafael J. Wysocki * @genpd: PM domain to power up. 2393f241775SRafael J. Wysocki */ 2403f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd) 2413f241775SRafael J. Wysocki { 2423f241775SRafael J. Wysocki int ret; 2433f241775SRafael J. Wysocki 2443f241775SRafael J. Wysocki mutex_lock(&genpd->lock); 2453f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 2463f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 2473f241775SRafael J. Wysocki return ret; 2485248051bSRafael J. Wysocki } 2495248051bSRafael J. Wysocki 2505248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 2515248051bSRafael J. Wysocki 2525248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 2535248051bSRafael J. Wysocki 2546ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 2556ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 2566ff7bb0dSRafael J. Wysocki { 2576ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 2586ff7bb0dSRafael J. Wysocki struct device *dev; 2596ff7bb0dSRafael J. Wysocki 2606ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 2616ff7bb0dSRafael J. Wysocki 2626ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 2636ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 2646ff7bb0dSRafael J. Wysocki if (!dev) { 2656ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 2666ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2676ff7bb0dSRafael J. Wysocki } 2686ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 2696ff7bb0dSRafael J. Wysocki 2706ff7bb0dSRafael J. Wysocki for (;;) { 2716ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 2726ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 2736ff7bb0dSRafael J. Wysocki 2746ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 2756ff7bb0dSRafael J. Wysocki 2766ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 2776ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 2786ff7bb0dSRafael J. Wysocki if (pdd) { 2796ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 2806ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 2816ff7bb0dSRafael J. Wysocki } else { 2826ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 2836ff7bb0dSRafael J. Wysocki } 2846ff7bb0dSRafael J. Wysocki 2856ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 2866ff7bb0dSRafael J. Wysocki 2876ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 2886ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 2896ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2906ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 2916ff7bb0dSRafael J. Wysocki } 2926ff7bb0dSRafael J. Wysocki 2936ff7bb0dSRafael J. Wysocki dev = dev->parent; 2946ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 2956ff7bb0dSRafael J. Wysocki break; 2966ff7bb0dSRafael J. Wysocki } 2976ff7bb0dSRafael J. Wysocki 2986ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2996ff7bb0dSRafael J. Wysocki } 3006ff7bb0dSRafael J. Wysocki 3015248051bSRafael J. Wysocki /** 302f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 3034605ab65SRafael J. Wysocki * @pdd: Domain data of the device to save the state of. 304f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 305f721889fSRafael J. Wysocki */ 3064605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd, 307f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 30817b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 309f721889fSRafael J. Wysocki { 310cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3114605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 312f721889fSRafael J. Wysocki int ret = 0; 313f721889fSRafael J. Wysocki 314cd0ea672SRafael J. Wysocki if (gpd_data->need_restore) 315f721889fSRafael J. Wysocki return 0; 316f721889fSRafael J. Wysocki 31717b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 31817b75ecaSRafael J. Wysocki 319d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 320ecf00475SRafael J. Wysocki ret = genpd_save_dev(genpd, dev); 321d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 322f721889fSRafael J. Wysocki 32317b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 32417b75ecaSRafael J. Wysocki 325f721889fSRafael J. Wysocki if (!ret) 326cd0ea672SRafael J. Wysocki gpd_data->need_restore = true; 327f721889fSRafael J. Wysocki 328f721889fSRafael J. Wysocki return ret; 329f721889fSRafael J. Wysocki } 330f721889fSRafael J. Wysocki 331f721889fSRafael J. Wysocki /** 332f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 3334605ab65SRafael J. Wysocki * @pdd: Domain data of the device to restore the state of. 334f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 335f721889fSRafael J. Wysocki */ 3364605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd, 337f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 33817b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 339f721889fSRafael J. Wysocki { 340cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3414605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 342f721889fSRafael J. Wysocki 343cd0ea672SRafael J. Wysocki if (!gpd_data->need_restore) 344f721889fSRafael J. Wysocki return; 345f721889fSRafael J. Wysocki 34617b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 34717b75ecaSRafael J. Wysocki 348d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 349ecf00475SRafael J. Wysocki genpd_restore_dev(genpd, dev); 350d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 351f721889fSRafael J. Wysocki 35217b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 35317b75ecaSRafael J. Wysocki 354cd0ea672SRafael J. Wysocki gpd_data->need_restore = false; 355f721889fSRafael J. Wysocki } 356f721889fSRafael J. Wysocki 357f721889fSRafael J. Wysocki /** 358c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 359c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 360c6d22b37SRafael J. Wysocki * 361c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 362c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 363c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 364c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 365c6d22b37SRafael J. Wysocki */ 366c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 367c6d22b37SRafael J. Wysocki { 36817877eb5SRafael J. Wysocki return genpd->status == GPD_STATE_WAIT_MASTER 3693f241775SRafael J. Wysocki || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 370c6d22b37SRafael J. Wysocki } 371c6d22b37SRafael J. Wysocki 372c6d22b37SRafael J. Wysocki /** 37356375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 37456375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 37556375fd4SRafael J. Wysocki * 37656375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 37756375fd4SRafael J. Wysocki * before. 37856375fd4SRafael J. Wysocki */ 3790bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 38056375fd4SRafael J. Wysocki { 38156375fd4SRafael J. Wysocki if (!work_pending(&genpd->power_off_work)) 38256375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 38356375fd4SRafael J. Wysocki } 38456375fd4SRafael J. Wysocki 38556375fd4SRafael J. Wysocki /** 386f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 387f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 388f721889fSRafael J. Wysocki * 389f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 390f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 391f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 392f721889fSRafael J. Wysocki */ 393f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 39417b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 395f721889fSRafael J. Wysocki { 3964605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 3975063ce15SRafael J. Wysocki struct gpd_link *link; 398f721889fSRafael J. Wysocki unsigned int not_suspended; 399c6d22b37SRafael J. Wysocki int ret = 0; 400f721889fSRafael J. Wysocki 401c6d22b37SRafael J. Wysocki start: 402c6d22b37SRafael J. Wysocki /* 403c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 404c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 4055063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 406c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 4073f241775SRafael J. Wysocki * (4) System suspend is in progress. 408c6d22b37SRafael J. Wysocki */ 4093f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 41017877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 4113f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 412f721889fSRafael J. Wysocki return 0; 413f721889fSRafael J. Wysocki 414c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 415f721889fSRafael J. Wysocki return -EBUSY; 416f721889fSRafael J. Wysocki 417f721889fSRafael J. Wysocki not_suspended = 0; 4184605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 4190aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 4201e78a0c7SRafael J. Wysocki || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on)) 421f721889fSRafael J. Wysocki not_suspended++; 422f721889fSRafael J. Wysocki 423f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 424f721889fSRafael J. Wysocki return -EBUSY; 425f721889fSRafael J. Wysocki 426c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 427c6d22b37SRafael J. Wysocki /* 428c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 429c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 430c6d22b37SRafael J. Wysocki */ 431c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 432c6d22b37SRafael J. Wysocki return 0; 433c6d22b37SRafael J. Wysocki } 434c6d22b37SRafael J. Wysocki 435f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 436f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 437f721889fSRafael J. Wysocki return -EAGAIN; 438f721889fSRafael J. Wysocki } 439f721889fSRafael J. Wysocki 44017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 441c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 44217b75ecaSRafael J. Wysocki 4434605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 4443c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 4454605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 4463f241775SRafael J. Wysocki 4473f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 4483f241775SRafael J. Wysocki goto out; 4493f241775SRafael J. Wysocki 450697a7f37SRafael J. Wysocki if (ret) { 451697a7f37SRafael J. Wysocki genpd_set_active(genpd); 452697a7f37SRafael J. Wysocki goto out; 453697a7f37SRafael J. Wysocki } 454f721889fSRafael J. Wysocki 455c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 456c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 457c6d22b37SRafael J. Wysocki goto start; 458c6d22b37SRafael J. Wysocki } 459c6d22b37SRafael J. Wysocki } 46017b75ecaSRafael J. Wysocki 4613c07cbc4SRafael J. Wysocki if (genpd->power_off) { 4620140d8bdSRafael J. Wysocki ktime_t time_start; 4630140d8bdSRafael J. Wysocki s64 elapsed_ns; 4640140d8bdSRafael J. Wysocki 4653c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 4663c07cbc4SRafael J. Wysocki ret = -EBUSY; 467c6d22b37SRafael J. Wysocki goto out; 468c6d22b37SRafael J. Wysocki } 46917b75ecaSRafael J. Wysocki 4700140d8bdSRafael J. Wysocki time_start = ktime_get(); 4710140d8bdSRafael J. Wysocki 4723c07cbc4SRafael J. Wysocki /* 4735063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 4745063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 4753c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 4763c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 4773c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 4783c07cbc4SRafael J. Wysocki * happen very often). 4793c07cbc4SRafael J. Wysocki */ 480d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 481d2805402SRafael J. Wysocki if (ret == -EBUSY) { 482d2805402SRafael J. Wysocki genpd_set_active(genpd); 483d2805402SRafael J. Wysocki goto out; 484d2805402SRafael J. Wysocki } 4850140d8bdSRafael J. Wysocki 4860140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 487e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_off_latency_ns) { 4880140d8bdSRafael J. Wysocki genpd->power_off_latency_ns = elapsed_ns; 4896ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 490e84b2c20SRafael J. Wysocki if (genpd->name) 491e84b2c20SRafael J. Wysocki pr_warning("%s: Power-off latency exceeded, " 492e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 493e84b2c20SRafael J. Wysocki elapsed_ns); 494e84b2c20SRafael J. Wysocki } 495d2805402SRafael J. Wysocki } 496f721889fSRafael J. Wysocki 49717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 498221e9b58SRafael J. Wysocki 4995063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5005063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 5015063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 5025063ce15SRafael J. Wysocki } 50317b75ecaSRafael J. Wysocki 504c6d22b37SRafael J. Wysocki out: 505c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 506c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 507c6d22b37SRafael J. Wysocki return ret; 508f721889fSRafael J. Wysocki } 509f721889fSRafael J. Wysocki 510f721889fSRafael J. Wysocki /** 511f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 512f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 513f721889fSRafael J. Wysocki */ 514f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 515f721889fSRafael J. Wysocki { 516f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 517f721889fSRafael J. Wysocki 518f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 519f721889fSRafael J. Wysocki 52017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 521f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 52217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 523f721889fSRafael J. Wysocki } 524f721889fSRafael J. Wysocki 525f721889fSRafael J. Wysocki /** 526f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 527f721889fSRafael J. Wysocki * @dev: Device to suspend. 528f721889fSRafael J. Wysocki * 529f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 530f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 531f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 532f721889fSRafael J. Wysocki */ 533f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 534f721889fSRafael J. Wysocki { 535f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 536b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 537d5e4cbfeSRafael J. Wysocki int ret; 538f721889fSRafael J. Wysocki 539f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 540f721889fSRafael J. Wysocki 5415248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5425248051bSRafael J. Wysocki if (IS_ERR(genpd)) 543f721889fSRafael J. Wysocki return -EINVAL; 544f721889fSRafael J. Wysocki 5450aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 5460aa2a221SRafael J. Wysocki 5471e78a0c7SRafael J. Wysocki if (dev_gpd_data(dev)->always_on) 5481e78a0c7SRafael J. Wysocki return -EBUSY; 5491e78a0c7SRafael J. Wysocki 550b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 551b02c999aSRafael J. Wysocki if (stop_ok && !stop_ok(dev)) 552b02c999aSRafael J. Wysocki return -EBUSY; 553b02c999aSRafael J. Wysocki 554d5e4cbfeSRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 555f721889fSRafael J. Wysocki if (ret) 55617b75ecaSRafael J. Wysocki return ret; 55717b75ecaSRafael J. Wysocki 5580aa2a221SRafael J. Wysocki /* 5590aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 5600aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 5610aa2a221SRafael J. Wysocki */ 5620aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 5630aa2a221SRafael J. Wysocki return 0; 5640aa2a221SRafael J. Wysocki 565c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 566f721889fSRafael J. Wysocki genpd->in_progress++; 567f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 568f721889fSRafael J. Wysocki genpd->in_progress--; 569c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 570f721889fSRafael J. Wysocki 571f721889fSRafael J. Wysocki return 0; 572f721889fSRafael J. Wysocki } 573f721889fSRafael J. Wysocki 574f721889fSRafael J. Wysocki /** 575f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 576f721889fSRafael J. Wysocki * @dev: Device to resume. 577f721889fSRafael J. Wysocki * 578f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 579f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 580f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 581f721889fSRafael J. Wysocki */ 582f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 583f721889fSRafael J. Wysocki { 584f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 585c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 586f721889fSRafael J. Wysocki int ret; 587f721889fSRafael J. Wysocki 588f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 589f721889fSRafael J. Wysocki 5905248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5915248051bSRafael J. Wysocki if (IS_ERR(genpd)) 592f721889fSRafael J. Wysocki return -EINVAL; 593f721889fSRafael J. Wysocki 5940aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 5950aa2a221SRafael J. Wysocki 5960aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 5970aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 5980aa2a221SRafael J. Wysocki goto out; 5990aa2a221SRafael J. Wysocki 600c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 6013f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 6023f241775SRafael J. Wysocki if (ret) { 6033f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 6043f241775SRafael J. Wysocki return ret; 6053f241775SRafael J. Wysocki } 60617b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 607c6d22b37SRafael J. Wysocki genpd->resume_count++; 608c6d22b37SRafael J. Wysocki for (;;) { 609c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 610c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 611c6d22b37SRafael J. Wysocki /* 612c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 613c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 614c6d22b37SRafael J. Wysocki * not wait. 615c6d22b37SRafael J. Wysocki */ 616c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 617c6d22b37SRafael J. Wysocki break; 618c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 619c6d22b37SRafael J. Wysocki 620c6d22b37SRafael J. Wysocki schedule(); 621c6d22b37SRafael J. Wysocki 622c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 623c6d22b37SRafael J. Wysocki } 624c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 625cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 626c6d22b37SRafael J. Wysocki genpd->resume_count--; 627c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 62817b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 629c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 63017b75ecaSRafael J. Wysocki 6310aa2a221SRafael J. Wysocki out: 632d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 633f721889fSRafael J. Wysocki 634f721889fSRafael J. Wysocki return 0; 635f721889fSRafael J. Wysocki } 636f721889fSRafael J. Wysocki 63717f2ae7fSRafael J. Wysocki /** 63817f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 63917f2ae7fSRafael J. Wysocki */ 64017f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 64117f2ae7fSRafael J. Wysocki { 64217f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 64317f2ae7fSRafael J. Wysocki 64417f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 64517f2ae7fSRafael J. Wysocki 64617f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 64717f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 64817f2ae7fSRafael J. Wysocki 64917f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 65017f2ae7fSRafael J. Wysocki } 65117f2ae7fSRafael J. Wysocki 652f721889fSRafael J. Wysocki #else 653f721889fSRafael J. Wysocki 6546ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 6556ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 6566ff7bb0dSRafael J. Wysocki { 6576ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 6586ff7bb0dSRafael J. Wysocki } 6596ff7bb0dSRafael J. Wysocki 660f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 661f721889fSRafael J. Wysocki 662f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 663f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 664f721889fSRafael J. Wysocki 665f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 666f721889fSRafael J. Wysocki 667596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 668596ba34bSRafael J. Wysocki 669d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 670d5e4cbfeSRafael J. Wysocki struct device *dev) 671d5e4cbfeSRafael J. Wysocki { 672d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 673d5e4cbfeSRafael J. Wysocki } 674d5e4cbfeSRafael J. Wysocki 675d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) 676d23b9b00SRafael J. Wysocki { 677d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); 678d23b9b00SRafael J. Wysocki } 679d23b9b00SRafael J. Wysocki 680d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) 681d23b9b00SRafael J. Wysocki { 682d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); 683d23b9b00SRafael J. Wysocki } 684d23b9b00SRafael J. Wysocki 685d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) 686d23b9b00SRafael J. Wysocki { 687d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); 688d23b9b00SRafael J. Wysocki } 689d23b9b00SRafael J. Wysocki 690d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) 691d23b9b00SRafael J. Wysocki { 692d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume, dev); 693d23b9b00SRafael J. Wysocki } 694d23b9b00SRafael J. Wysocki 695d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) 696d23b9b00SRafael J. Wysocki { 697d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); 698d23b9b00SRafael J. Wysocki } 699d23b9b00SRafael J. Wysocki 700d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) 701d23b9b00SRafael J. Wysocki { 702d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); 703d23b9b00SRafael J. Wysocki } 704d23b9b00SRafael J. Wysocki 705d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) 706d23b9b00SRafael J. Wysocki { 707d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); 708d23b9b00SRafael J. Wysocki } 709d23b9b00SRafael J. Wysocki 710d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) 711d23b9b00SRafael J. Wysocki { 712d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); 713d23b9b00SRafael J. Wysocki } 714d23b9b00SRafael J. Wysocki 715596ba34bSRafael J. Wysocki /** 7165063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 717596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 718596ba34bSRafael J. Wysocki * 719596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7205063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 721596ba34bSRafael J. Wysocki * 722596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 723596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 724596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 725596ba34bSRafael J. Wysocki */ 726596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 727596ba34bSRafael J. Wysocki { 7285063ce15SRafael J. Wysocki struct gpd_link *link; 729596ba34bSRafael J. Wysocki 73017b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 731596ba34bSRafael J. Wysocki return; 732596ba34bSRafael J. Wysocki 733c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 734c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 735596ba34bSRafael J. Wysocki return; 736596ba34bSRafael J. Wysocki 737596ba34bSRafael J. Wysocki if (genpd->power_off) 738596ba34bSRafael J. Wysocki genpd->power_off(genpd); 739596ba34bSRafael J. Wysocki 74017b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7415063ce15SRafael J. Wysocki 7425063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7435063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 7445063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 745596ba34bSRafael J. Wysocki } 746596ba34bSRafael J. Wysocki } 747596ba34bSRafael J. Wysocki 748596ba34bSRafael J. Wysocki /** 7494ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 7504ecd6e65SRafael J. Wysocki * @dev: Device to check. 7514ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 7524ecd6e65SRafael J. Wysocki * 7534ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 7544ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 7554ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 7564ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 7574ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 7584ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 7594ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 7604ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 7614ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 7624ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 7634ecd6e65SRafael J. Wysocki */ 7644ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 7654ecd6e65SRafael J. Wysocki { 7664ecd6e65SRafael J. Wysocki bool active_wakeup; 7674ecd6e65SRafael J. Wysocki 7684ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 7694ecd6e65SRafael J. Wysocki return false; 7704ecd6e65SRafael J. Wysocki 771d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 7724ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 7734ecd6e65SRafael J. Wysocki } 7744ecd6e65SRafael J. Wysocki 7754ecd6e65SRafael J. Wysocki /** 776596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 777596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 778596ba34bSRafael J. Wysocki * 779596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 780596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 781596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 782596ba34bSRafael J. Wysocki * consisting of I/O devices. 783596ba34bSRafael J. Wysocki */ 784596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 785596ba34bSRafael J. Wysocki { 786596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 787b6c10c84SRafael J. Wysocki int ret; 788596ba34bSRafael J. Wysocki 789596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 790596ba34bSRafael J. Wysocki 791596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 792596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 793596ba34bSRafael J. Wysocki return -EINVAL; 794596ba34bSRafael J. Wysocki 79517b75ecaSRafael J. Wysocki /* 79617b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 79717b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 79817b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 79917b75ecaSRafael J. Wysocki */ 80017b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 80117b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 80217b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 80317b75ecaSRafael J. Wysocki 80417b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 80517b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 80617b75ecaSRafael J. Wysocki return -EBUSY; 80717b75ecaSRafael J. Wysocki } 80817b75ecaSRafael J. Wysocki 8094ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8104ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8114ecd6e65SRafael J. Wysocki 81217b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 813596ba34bSRafael J. Wysocki 81465533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 81565533bbfSRafael J. Wysocki genpd->suspended_count = 0; 81617b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 81765533bbfSRafael J. Wysocki } 81817b75ecaSRafael J. Wysocki 81917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 820596ba34bSRafael J. Wysocki 821596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 82217b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 823596ba34bSRafael J. Wysocki return 0; 824596ba34bSRafael J. Wysocki } 825596ba34bSRafael J. Wysocki 826596ba34bSRafael J. Wysocki /* 82717b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 82817b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 829d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 83017b75ecaSRafael J. Wysocki * to make it operational. 831596ba34bSRafael J. Wysocki */ 83217b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 833596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 834596ba34bSRafael J. Wysocki 835b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 836b6c10c84SRafael J. Wysocki if (ret) { 837b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 838b6c10c84SRafael J. Wysocki 839b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 840b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 841b6c10c84SRafael J. Wysocki 842b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 84317b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 844b6c10c84SRafael J. Wysocki } 84517b75ecaSRafael J. Wysocki 84617b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 847b6c10c84SRafael J. Wysocki return ret; 848596ba34bSRafael J. Wysocki } 849596ba34bSRafael J. Wysocki 850596ba34bSRafael J. Wysocki /** 851596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 852596ba34bSRafael J. Wysocki * @dev: Device to suspend. 853596ba34bSRafael J. Wysocki * 854596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 855596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 856596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 857596ba34bSRafael J. Wysocki */ 858596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 859596ba34bSRafael J. Wysocki { 860596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 861596ba34bSRafael J. Wysocki 862596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 863596ba34bSRafael J. Wysocki 864596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 865596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 866596ba34bSRafael J. Wysocki return -EINVAL; 867596ba34bSRafael J. Wysocki 868d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); 869596ba34bSRafael J. Wysocki } 870596ba34bSRafael J. Wysocki 871596ba34bSRafael J. Wysocki /** 8720496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 873596ba34bSRafael J. Wysocki * @dev: Device to suspend. 874596ba34bSRafael J. Wysocki * 875596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 876596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 877596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 878596ba34bSRafael J. Wysocki */ 8790496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 880596ba34bSRafael J. Wysocki { 881596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 882596ba34bSRafael J. Wysocki 883596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 884596ba34bSRafael J. Wysocki 885596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 886596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 887596ba34bSRafael J. Wysocki return -EINVAL; 888596ba34bSRafael J. Wysocki 8890496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); 8900496c8aeSRafael J. Wysocki } 891596ba34bSRafael J. Wysocki 8920496c8aeSRafael J. Wysocki /** 8930496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 8940496c8aeSRafael J. Wysocki * @dev: Device to suspend. 8950496c8aeSRafael J. Wysocki * 8960496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 8970496c8aeSRafael J. Wysocki * been stopped. 8980496c8aeSRafael J. Wysocki */ 8990496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 9000496c8aeSRafael J. Wysocki { 9010496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 902596ba34bSRafael J. Wysocki 9030496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9040496c8aeSRafael J. Wysocki 9050496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9060496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9070496c8aeSRafael J. Wysocki return -EINVAL; 9080496c8aeSRafael J. Wysocki 9091e78a0c7SRafael J. Wysocki if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on 9100496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 911d4f2d87aSRafael J. Wysocki return 0; 912d4f2d87aSRafael J. Wysocki 913d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 914596ba34bSRafael J. Wysocki 915596ba34bSRafael J. Wysocki /* 916596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 917596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 918596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 919596ba34bSRafael J. Wysocki */ 920596ba34bSRafael J. Wysocki genpd->suspended_count++; 921596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 922596ba34bSRafael J. Wysocki 923596ba34bSRafael J. Wysocki return 0; 924596ba34bSRafael J. Wysocki } 925596ba34bSRafael J. Wysocki 926596ba34bSRafael J. Wysocki /** 9270496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 928596ba34bSRafael J. Wysocki * @dev: Device to resume. 929596ba34bSRafael J. Wysocki * 9300496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 931596ba34bSRafael J. Wysocki */ 932596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 933596ba34bSRafael J. Wysocki { 934596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 935596ba34bSRafael J. Wysocki 936596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 937596ba34bSRafael J. Wysocki 938596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 939596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 940596ba34bSRafael J. Wysocki return -EINVAL; 941596ba34bSRafael J. Wysocki 9421e78a0c7SRafael J. Wysocki if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on 943cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 944596ba34bSRafael J. Wysocki return 0; 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 pm_genpd_poweron(genpd); 952596ba34bSRafael J. Wysocki genpd->suspended_count--; 953596ba34bSRafael J. Wysocki 9540496c8aeSRafael J. Wysocki return genpd_start_dev(genpd, dev); 955596ba34bSRafael J. Wysocki } 956596ba34bSRafael J. Wysocki 957596ba34bSRafael J. Wysocki /** 9580496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 9590496c8aeSRafael J. Wysocki * @dev: Device to resume. 9600496c8aeSRafael J. Wysocki * 9610496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 9620496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9630496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9640496c8aeSRafael J. Wysocki * devices. 9650496c8aeSRafael J. Wysocki */ 9660496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 9670496c8aeSRafael J. Wysocki { 9680496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 9690496c8aeSRafael J. Wysocki 9700496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9710496c8aeSRafael J. Wysocki 9720496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9730496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9740496c8aeSRafael J. Wysocki return -EINVAL; 9750496c8aeSRafael J. Wysocki 9760496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); 9770496c8aeSRafael J. Wysocki } 9780496c8aeSRafael J. Wysocki 9790496c8aeSRafael J. Wysocki /** 9800496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 981596ba34bSRafael J. Wysocki * @dev: Device to resume. 982596ba34bSRafael J. Wysocki * 983596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 984596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 985596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 986596ba34bSRafael J. Wysocki */ 987596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 988596ba34bSRafael J. Wysocki { 989596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 990596ba34bSRafael J. Wysocki 991596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 992596ba34bSRafael J. Wysocki 993596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 994596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 995596ba34bSRafael J. Wysocki return -EINVAL; 996596ba34bSRafael J. Wysocki 997d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); 998596ba34bSRafael J. Wysocki } 999596ba34bSRafael J. Wysocki 1000596ba34bSRafael J. Wysocki /** 10010496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 1002596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1003596ba34bSRafael J. Wysocki * 1004596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 1005596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1006596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1007596ba34bSRafael J. Wysocki */ 1008596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 1009596ba34bSRafael J. Wysocki { 1010596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1011596ba34bSRafael J. Wysocki 1012596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1013596ba34bSRafael J. Wysocki 1014596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1015596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1016596ba34bSRafael J. Wysocki return -EINVAL; 1017596ba34bSRafael J. Wysocki 1018d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); 1019596ba34bSRafael J. Wysocki } 1020596ba34bSRafael J. Wysocki 1021596ba34bSRafael J. Wysocki /** 10220496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 10230496c8aeSRafael J. Wysocki * @dev: Device to freeze. 10240496c8aeSRafael J. Wysocki * 10250496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 10260496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10270496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10280496c8aeSRafael J. Wysocki * devices. 10290496c8aeSRafael J. Wysocki */ 10300496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 10310496c8aeSRafael J. Wysocki { 10320496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10330496c8aeSRafael J. Wysocki 10340496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10350496c8aeSRafael J. Wysocki 10360496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10370496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10380496c8aeSRafael J. Wysocki return -EINVAL; 10390496c8aeSRafael J. Wysocki 10400496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); 10410496c8aeSRafael J. Wysocki } 10420496c8aeSRafael J. Wysocki 10430496c8aeSRafael J. Wysocki /** 10440496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1045596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1046596ba34bSRafael J. Wysocki * 1047596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1048596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1049596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1050596ba34bSRafael J. Wysocki * devices. 1051596ba34bSRafael J. Wysocki */ 1052596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1053596ba34bSRafael J. Wysocki { 1054596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1055596ba34bSRafael J. Wysocki 1056596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1057596ba34bSRafael J. Wysocki 1058596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1059596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1060596ba34bSRafael J. Wysocki return -EINVAL; 1061596ba34bSRafael J. Wysocki 10621e78a0c7SRafael J. Wysocki return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? 10631e78a0c7SRafael J. Wysocki 0 : genpd_stop_dev(genpd, dev); 1064596ba34bSRafael J. Wysocki } 1065596ba34bSRafael J. Wysocki 1066596ba34bSRafael J. Wysocki /** 10670496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1068596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1069596ba34bSRafael J. Wysocki * 10700496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 10710496c8aeSRafael J. Wysocki * before the system transition. 1072596ba34bSRafael J. Wysocki */ 1073596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1074596ba34bSRafael J. Wysocki { 1075596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1076596ba34bSRafael J. Wysocki 1077596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1078596ba34bSRafael J. Wysocki 1079596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1080596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1081596ba34bSRafael J. Wysocki return -EINVAL; 1082596ba34bSRafael J. Wysocki 10831e78a0c7SRafael J. Wysocki return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? 10841e78a0c7SRafael J. Wysocki 0 : genpd_start_dev(genpd, dev); 10850496c8aeSRafael J. Wysocki } 1086596ba34bSRafael J. Wysocki 10870496c8aeSRafael J. Wysocki /** 10880496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 10890496c8aeSRafael J. Wysocki * @dev: Device to thaw. 10900496c8aeSRafael J. Wysocki * 10910496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 10920496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10930496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10940496c8aeSRafael J. Wysocki * devices. 10950496c8aeSRafael J. Wysocki */ 10960496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 10970496c8aeSRafael J. Wysocki { 10980496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1099596ba34bSRafael J. Wysocki 11000496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 11010496c8aeSRafael J. Wysocki 11020496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11030496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11040496c8aeSRafael J. Wysocki return -EINVAL; 11050496c8aeSRafael J. Wysocki 11060496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); 1107596ba34bSRafael J. Wysocki } 1108596ba34bSRafael J. Wysocki 1109596ba34bSRafael J. Wysocki /** 1110596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1111596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1112596ba34bSRafael J. Wysocki * 1113596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1114596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1115596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1116596ba34bSRafael J. Wysocki */ 1117596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1118596ba34bSRafael J. Wysocki { 1119596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1120596ba34bSRafael J. Wysocki 1121596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1122596ba34bSRafael J. Wysocki 1123596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1124596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1125596ba34bSRafael J. Wysocki return -EINVAL; 1126596ba34bSRafael J. Wysocki 1127d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); 1128596ba34bSRafael J. Wysocki } 1129596ba34bSRafael J. Wysocki 1130596ba34bSRafael J. Wysocki /** 11310496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1132596ba34bSRafael J. Wysocki * @dev: Device to resume. 1133596ba34bSRafael J. Wysocki * 11340496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 11350496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1136596ba34bSRafael J. Wysocki */ 1137596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1138596ba34bSRafael J. Wysocki { 1139596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1140596ba34bSRafael J. Wysocki 1141596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1142596ba34bSRafael J. Wysocki 1143596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1144596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1145596ba34bSRafael J. Wysocki return -EINVAL; 1146596ba34bSRafael J. Wysocki 1147596ba34bSRafael J. Wysocki /* 1148596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1149596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1150596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 115165533bbfSRafael J. Wysocki * 115265533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 115365533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 115465533bbfSRafael J. Wysocki */ 115565533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 115665533bbfSRafael J. Wysocki /* 115765533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 115865533bbfSRafael J. Wysocki * so make it appear as powered off to pm_genpd_poweron(), so 115965533bbfSRafael J. Wysocki * that it tries to power it on in case it was really off. 1160596ba34bSRafael J. Wysocki */ 116117b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1162596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1163596ba34bSRafael J. Wysocki /* 116465533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 116565533bbfSRafael J. Wysocki * sure it will be off going forward. 1166596ba34bSRafael J. Wysocki */ 1167596ba34bSRafael J. Wysocki if (genpd->power_off) 1168596ba34bSRafael J. Wysocki genpd->power_off(genpd); 116965533bbfSRafael J. Wysocki 1170596ba34bSRafael J. Wysocki return 0; 1171596ba34bSRafael J. Wysocki } 117265533bbfSRafael J. Wysocki } 1173596ba34bSRafael J. Wysocki 117418dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 117518dd2eceSRafael J. Wysocki return 0; 117618dd2eceSRafael J. Wysocki 1177596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 1178596ba34bSRafael J. Wysocki 11791e78a0c7SRafael J. Wysocki return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev); 1180596ba34bSRafael J. Wysocki } 1181596ba34bSRafael J. Wysocki 1182596ba34bSRafael J. Wysocki /** 1183596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1184596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1185596ba34bSRafael J. Wysocki * 1186596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1187596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1188596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1189596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1190596ba34bSRafael J. Wysocki */ 1191596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1192596ba34bSRafael J. Wysocki { 1193596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1194596ba34bSRafael J. Wysocki bool run_complete; 1195596ba34bSRafael J. Wysocki 1196596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1197596ba34bSRafael J. Wysocki 1198596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1199596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1200596ba34bSRafael J. Wysocki return; 1201596ba34bSRafael J. Wysocki 1202596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1203596ba34bSRafael J. Wysocki 1204596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1205596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1206596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1207596ba34bSRafael J. Wysocki 1208596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1209596ba34bSRafael J. Wysocki 1210596ba34bSRafael J. Wysocki if (run_complete) { 1211596ba34bSRafael J. Wysocki pm_generic_complete(dev); 12126f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1213596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 12146f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 1215596ba34bSRafael J. Wysocki } 1216596ba34bSRafael J. Wysocki } 1217596ba34bSRafael J. Wysocki 1218596ba34bSRafael J. Wysocki #else 1219596ba34bSRafael J. Wysocki 1220596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1221596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 12220496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1223596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 12240496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1225596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1226596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1227596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 12280496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1229596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 12300496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1231596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1232596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1233596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1234596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1235596ba34bSRafael J. Wysocki 1236596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1237596ba34bSRafael J. Wysocki 1238f721889fSRafael J. Wysocki /** 1239b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1240f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1241f721889fSRafael J. Wysocki * @dev: Device to be added. 1242b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1243f721889fSRafael J. Wysocki */ 1244b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1245b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1246f721889fSRafael J. Wysocki { 1247cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12484605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1249f721889fSRafael J. Wysocki int ret = 0; 1250f721889fSRafael J. Wysocki 1251f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1252f721889fSRafael J. Wysocki 1253f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1254f721889fSRafael J. Wysocki return -EINVAL; 1255f721889fSRafael J. Wysocki 12566ff7bb0dSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 12576ff7bb0dSRafael J. Wysocki if (!gpd_data) 12586ff7bb0dSRafael J. Wysocki return -ENOMEM; 12596ff7bb0dSRafael J. Wysocki 12606ff7bb0dSRafael J. Wysocki mutex_init(&gpd_data->lock); 12616ff7bb0dSRafael J. Wysocki gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 12626ff7bb0dSRafael J. Wysocki dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12636ff7bb0dSRafael J. Wysocki 126417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1265f721889fSRafael J. Wysocki 1266596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1267596ba34bSRafael J. Wysocki ret = -EAGAIN; 1268596ba34bSRafael J. Wysocki goto out; 1269596ba34bSRafael J. Wysocki } 1270596ba34bSRafael J. Wysocki 12714605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 12724605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1273f721889fSRafael J. Wysocki ret = -EINVAL; 1274f721889fSRafael J. Wysocki goto out; 1275f721889fSRafael J. Wysocki } 1276f721889fSRafael J. Wysocki 1277596ba34bSRafael J. Wysocki genpd->device_count++; 12786ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1279f721889fSRafael J. Wysocki 12804605ab65SRafael J. Wysocki dev_pm_get_subsys_data(dev); 12816ff7bb0dSRafael J. Wysocki 12826ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 12836ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 12846ff7bb0dSRafael J. Wysocki dev->pm_domain = &genpd->domain; 1285cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 1286cd0ea672SRafael J. Wysocki gpd_data->base.dev = dev; 1287cd0ea672SRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 1288ca1d72f0SRafael J. Wysocki gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 1289b02c999aSRafael J. Wysocki if (td) 1290b02c999aSRafael J. Wysocki gpd_data->td = *td; 1291f721889fSRafael J. Wysocki 12926ff7bb0dSRafael J. Wysocki gpd_data->td.constraint_changed = true; 12936ff7bb0dSRafael J. Wysocki gpd_data->td.effective_constraint_ns = -1; 12946ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 12956ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 12966ff7bb0dSRafael J. Wysocki 12976ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 12986ff7bb0dSRafael J. Wysocki 12996ff7bb0dSRafael J. Wysocki return 0; 13006ff7bb0dSRafael J. Wysocki 1301f721889fSRafael J. Wysocki out: 130217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1303f721889fSRafael J. Wysocki 13046ff7bb0dSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13056ff7bb0dSRafael J. Wysocki kfree(gpd_data); 1306f721889fSRafael J. Wysocki return ret; 1307f721889fSRafael J. Wysocki } 1308f721889fSRafael J. Wysocki 1309f721889fSRafael J. Wysocki /** 1310c8aa130bSThomas Abraham * __pm_genpd_of_add_device - Add a device to an I/O PM domain. 1311c8aa130bSThomas Abraham * @genpd_node: Device tree node pointer representing a PM domain to which the 1312c8aa130bSThomas Abraham * the device is added to. 1313c8aa130bSThomas Abraham * @dev: Device to be added. 1314c8aa130bSThomas Abraham * @td: Set of PM QoS timing parameters to attach to the device. 1315c8aa130bSThomas Abraham */ 1316c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, 1317c8aa130bSThomas Abraham struct gpd_timing_data *td) 1318c8aa130bSThomas Abraham { 1319c8aa130bSThomas Abraham struct generic_pm_domain *genpd = NULL, *gpd; 1320c8aa130bSThomas Abraham 1321c8aa130bSThomas Abraham dev_dbg(dev, "%s()\n", __func__); 1322c8aa130bSThomas Abraham 1323c8aa130bSThomas Abraham if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) 1324c8aa130bSThomas Abraham return -EINVAL; 1325c8aa130bSThomas Abraham 1326c8aa130bSThomas Abraham mutex_lock(&gpd_list_lock); 1327c8aa130bSThomas Abraham list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1328c8aa130bSThomas Abraham if (gpd->of_node == genpd_node) { 1329c8aa130bSThomas Abraham genpd = gpd; 1330c8aa130bSThomas Abraham break; 1331c8aa130bSThomas Abraham } 1332c8aa130bSThomas Abraham } 1333c8aa130bSThomas Abraham mutex_unlock(&gpd_list_lock); 1334c8aa130bSThomas Abraham 1335c8aa130bSThomas Abraham if (!genpd) 1336c8aa130bSThomas Abraham return -EINVAL; 1337c8aa130bSThomas Abraham 1338c8aa130bSThomas Abraham return __pm_genpd_add_device(genpd, dev, td); 1339c8aa130bSThomas Abraham } 1340c8aa130bSThomas Abraham 1341c8aa130bSThomas Abraham /** 1342f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1343f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1344f721889fSRafael J. Wysocki * @dev: Device to be removed. 1345f721889fSRafael J. Wysocki */ 1346f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1347f721889fSRafael J. Wysocki struct device *dev) 1348f721889fSRafael J. Wysocki { 13496ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13504605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1351efa69025SRafael J. Wysocki int ret = 0; 1352f721889fSRafael J. Wysocki 1353f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1354f721889fSRafael J. Wysocki 1355efa69025SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) 1356efa69025SRafael J. Wysocki || IS_ERR_OR_NULL(dev->pm_domain) 1357efa69025SRafael J. Wysocki || pd_to_genpd(dev->pm_domain) != genpd) 1358f721889fSRafael J. Wysocki return -EINVAL; 1359f721889fSRafael J. Wysocki 136017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1361f721889fSRafael J. Wysocki 1362596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1363596ba34bSRafael J. Wysocki ret = -EAGAIN; 1364596ba34bSRafael J. Wysocki goto out; 1365596ba34bSRafael J. Wysocki } 1366596ba34bSRafael J. Wysocki 13676ff7bb0dSRafael J. Wysocki genpd->device_count--; 13686ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 13696ff7bb0dSRafael J. Wysocki 13706ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1371f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1372efa69025SRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1373efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 1374efa69025SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 13756ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1376f721889fSRafael J. Wysocki 13776ff7bb0dSRafael J. Wysocki gpd_data = to_gpd_data(pdd); 13786ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 13796ff7bb0dSRafael J. Wysocki pdd->dev = NULL; 13806ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 13816ff7bb0dSRafael J. Wysocki 13826ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 13836ff7bb0dSRafael J. Wysocki 13846ff7bb0dSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13856ff7bb0dSRafael J. Wysocki kfree(gpd_data); 13866ff7bb0dSRafael J. Wysocki dev_pm_put_subsys_data(dev); 13876ff7bb0dSRafael J. Wysocki return 0; 1388f721889fSRafael J. Wysocki 1389596ba34bSRafael J. Wysocki out: 139017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1391f721889fSRafael J. Wysocki 1392f721889fSRafael J. Wysocki return ret; 1393f721889fSRafael J. Wysocki } 1394f721889fSRafael J. Wysocki 1395f721889fSRafael J. Wysocki /** 13961e78a0c7SRafael J. Wysocki * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device. 13971e78a0c7SRafael J. Wysocki * @dev: Device to set/unset the flag for. 13981e78a0c7SRafael J. Wysocki * @val: The new value of the device's "always on" flag. 13991e78a0c7SRafael J. Wysocki */ 14001e78a0c7SRafael J. Wysocki void pm_genpd_dev_always_on(struct device *dev, bool val) 14011e78a0c7SRafael J. Wysocki { 14021e78a0c7SRafael J. Wysocki struct pm_subsys_data *psd; 14031e78a0c7SRafael J. Wysocki unsigned long flags; 14041e78a0c7SRafael J. Wysocki 14051e78a0c7SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 14061e78a0c7SRafael J. Wysocki 14071e78a0c7SRafael J. Wysocki psd = dev_to_psd(dev); 14081e78a0c7SRafael J. Wysocki if (psd && psd->domain_data) 14091e78a0c7SRafael J. Wysocki to_gpd_data(psd->domain_data)->always_on = val; 14101e78a0c7SRafael J. Wysocki 14111e78a0c7SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 14121e78a0c7SRafael J. Wysocki } 14131e78a0c7SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); 14141e78a0c7SRafael J. Wysocki 14151e78a0c7SRafael J. Wysocki /** 1416ca1d72f0SRafael J. Wysocki * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. 1417ca1d72f0SRafael J. Wysocki * @dev: Device to set/unset the flag for. 1418ca1d72f0SRafael J. Wysocki * @val: The new value of the device's "need restore" flag. 1419ca1d72f0SRafael J. Wysocki */ 1420ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val) 1421ca1d72f0SRafael J. Wysocki { 1422ca1d72f0SRafael J. Wysocki struct pm_subsys_data *psd; 1423ca1d72f0SRafael J. Wysocki unsigned long flags; 1424ca1d72f0SRafael J. Wysocki 1425ca1d72f0SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 1426ca1d72f0SRafael J. Wysocki 1427ca1d72f0SRafael J. Wysocki psd = dev_to_psd(dev); 1428ca1d72f0SRafael J. Wysocki if (psd && psd->domain_data) 1429ca1d72f0SRafael J. Wysocki to_gpd_data(psd->domain_data)->need_restore = val; 1430ca1d72f0SRafael J. Wysocki 1431ca1d72f0SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 1432ca1d72f0SRafael J. Wysocki } 1433ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); 1434ca1d72f0SRafael J. Wysocki 1435ca1d72f0SRafael J. Wysocki /** 1436f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1437f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1438bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1439f721889fSRafael J. Wysocki */ 1440f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1441bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1442f721889fSRafael J. Wysocki { 14435063ce15SRafael J. Wysocki struct gpd_link *link; 1444f721889fSRafael J. Wysocki int ret = 0; 1445f721889fSRafael J. Wysocki 1446bc0403ffSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1447f721889fSRafael J. Wysocki return -EINVAL; 1448f721889fSRafael J. Wysocki 144917b75ecaSRafael J. Wysocki start: 145017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1451bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1452f721889fSRafael J. Wysocki 1453bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1454bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1455bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 145617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 145717b75ecaSRafael J. Wysocki goto start; 145817b75ecaSRafael J. Wysocki } 145917b75ecaSRafael J. Wysocki 146017b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1461bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1462f721889fSRafael J. Wysocki ret = -EINVAL; 1463f721889fSRafael J. Wysocki goto out; 1464f721889fSRafael J. Wysocki } 1465f721889fSRafael J. Wysocki 14664fcac10dSHuang Ying list_for_each_entry(link, &genpd->master_links, master_node) { 1467bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1468f721889fSRafael J. Wysocki ret = -EINVAL; 1469f721889fSRafael J. Wysocki goto out; 1470f721889fSRafael J. Wysocki } 1471f721889fSRafael J. Wysocki } 1472f721889fSRafael J. Wysocki 14735063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 14745063ce15SRafael J. Wysocki if (!link) { 14755063ce15SRafael J. Wysocki ret = -ENOMEM; 14765063ce15SRafael J. Wysocki goto out; 14775063ce15SRafael J. Wysocki } 14785063ce15SRafael J. Wysocki link->master = genpd; 14795063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1480bc0403ffSRafael J. Wysocki link->slave = subdomain; 1481bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1482bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1483c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1484f721889fSRafael J. Wysocki 1485f721889fSRafael J. Wysocki out: 1486bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 148717b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1488f721889fSRafael J. Wysocki 1489f721889fSRafael J. Wysocki return ret; 1490f721889fSRafael J. Wysocki } 1491f721889fSRafael J. Wysocki 1492f721889fSRafael J. Wysocki /** 1493f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1494f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 14955063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1496f721889fSRafael J. Wysocki */ 1497f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 14985063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1499f721889fSRafael J. Wysocki { 15005063ce15SRafael J. Wysocki struct gpd_link *link; 1501f721889fSRafael J. Wysocki int ret = -EINVAL; 1502f721889fSRafael J. Wysocki 15035063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1504f721889fSRafael J. Wysocki return -EINVAL; 1505f721889fSRafael J. Wysocki 150617b75ecaSRafael J. Wysocki start: 150717b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1508f721889fSRafael J. Wysocki 15095063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 15105063ce15SRafael J. Wysocki if (link->slave != subdomain) 1511f721889fSRafael J. Wysocki continue; 1512f721889fSRafael J. Wysocki 1513f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1514f721889fSRafael J. Wysocki 151517b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 151617b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 151717b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 151817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 151917b75ecaSRafael J. Wysocki goto start; 152017b75ecaSRafael J. Wysocki } 152117b75ecaSRafael J. Wysocki 15225063ce15SRafael J. Wysocki list_del(&link->master_node); 15235063ce15SRafael J. Wysocki list_del(&link->slave_node); 15245063ce15SRafael J. Wysocki kfree(link); 152517b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1526f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1527f721889fSRafael J. Wysocki 1528f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1529f721889fSRafael J. Wysocki 1530f721889fSRafael J. Wysocki ret = 0; 1531f721889fSRafael J. Wysocki break; 1532f721889fSRafael J. Wysocki } 1533f721889fSRafael J. Wysocki 153417b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1535f721889fSRafael J. Wysocki 1536f721889fSRafael J. Wysocki return ret; 1537f721889fSRafael J. Wysocki } 1538f721889fSRafael J. Wysocki 1539f721889fSRafael J. Wysocki /** 1540d5e4cbfeSRafael J. Wysocki * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. 1541d5e4cbfeSRafael J. Wysocki * @dev: Device to add the callbacks to. 1542d5e4cbfeSRafael J. Wysocki * @ops: Set of callbacks to add. 1543b02c999aSRafael J. Wysocki * @td: Timing data to add to the device along with the callbacks (optional). 1544d5e4cbfeSRafael J. Wysocki */ 1545b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, 1546b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1547d5e4cbfeSRafael J. Wysocki { 1548d5e4cbfeSRafael J. Wysocki struct pm_domain_data *pdd; 1549d5e4cbfeSRafael J. Wysocki int ret = 0; 1550d5e4cbfeSRafael J. Wysocki 1551d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data && ops)) 1552d5e4cbfeSRafael J. Wysocki return -EINVAL; 1553d5e4cbfeSRafael J. Wysocki 1554d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1555d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1556d5e4cbfeSRafael J. Wysocki 1557d5e4cbfeSRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1558d5e4cbfeSRafael J. Wysocki if (pdd) { 1559d5e4cbfeSRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1560d5e4cbfeSRafael J. Wysocki 1561d5e4cbfeSRafael J. Wysocki gpd_data->ops = *ops; 1562b02c999aSRafael J. Wysocki if (td) 1563b02c999aSRafael J. Wysocki gpd_data->td = *td; 1564d5e4cbfeSRafael J. Wysocki } else { 1565d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1566d5e4cbfeSRafael J. Wysocki } 1567d5e4cbfeSRafael J. Wysocki 1568d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1569d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1570d5e4cbfeSRafael J. Wysocki 1571d5e4cbfeSRafael J. Wysocki return ret; 1572d5e4cbfeSRafael J. Wysocki } 1573d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); 1574d5e4cbfeSRafael J. Wysocki 1575d5e4cbfeSRafael J. Wysocki /** 1576b02c999aSRafael J. Wysocki * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. 1577d5e4cbfeSRafael J. Wysocki * @dev: Device to remove the callbacks from. 1578b02c999aSRafael J. Wysocki * @clear_td: If set, clear the device's timing data too. 1579d5e4cbfeSRafael J. Wysocki */ 1580b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) 1581d5e4cbfeSRafael J. Wysocki { 1582d5e4cbfeSRafael J. Wysocki struct pm_domain_data *pdd; 1583d5e4cbfeSRafael J. Wysocki int ret = 0; 1584d5e4cbfeSRafael J. Wysocki 1585d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data)) 1586d5e4cbfeSRafael J. Wysocki return -EINVAL; 1587d5e4cbfeSRafael J. Wysocki 1588d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1589d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1590d5e4cbfeSRafael J. Wysocki 1591d5e4cbfeSRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1592d5e4cbfeSRafael J. Wysocki if (pdd) { 1593d5e4cbfeSRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1594d5e4cbfeSRafael J. Wysocki 1595d5e4cbfeSRafael J. Wysocki gpd_data->ops = (struct gpd_dev_ops){ 0 }; 1596b02c999aSRafael J. Wysocki if (clear_td) 1597b02c999aSRafael J. Wysocki gpd_data->td = (struct gpd_timing_data){ 0 }; 1598d5e4cbfeSRafael J. Wysocki } else { 1599d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1600d5e4cbfeSRafael J. Wysocki } 1601d5e4cbfeSRafael J. Wysocki 1602d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1603d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1604d5e4cbfeSRafael J. Wysocki 1605d5e4cbfeSRafael J. Wysocki return ret; 1606d5e4cbfeSRafael J. Wysocki } 1607b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); 1608d5e4cbfeSRafael J. Wysocki 1609d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1610d23b9b00SRafael J. Wysocki 1611d5e4cbfeSRafael J. Wysocki /** 1612ecf00475SRafael J. Wysocki * pm_genpd_default_save_state - Default "save device state" for PM domians. 1613ecf00475SRafael J. Wysocki * @dev: Device to handle. 1614ecf00475SRafael J. Wysocki */ 1615ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1616ecf00475SRafael J. Wysocki { 1617ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1618ecf00475SRafael J. Wysocki 1619ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.save_state; 1620ecf00475SRafael J. Wysocki if (cb) 1621ecf00475SRafael J. Wysocki return cb(dev); 1622ecf00475SRafael J. Wysocki 16230b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 16240b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 16250b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 16260b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 16270b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 16280b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 16290b589741SRafael J. Wysocki else 16300b589741SRafael J. Wysocki cb = NULL; 1631ecf00475SRafael J. Wysocki 16320b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 16330b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 16340b589741SRafael J. Wysocki 16350b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1636ecf00475SRafael J. Wysocki } 1637ecf00475SRafael J. Wysocki 1638ecf00475SRafael J. Wysocki /** 1639ecf00475SRafael J. Wysocki * pm_genpd_default_restore_state - Default PM domians "restore device state". 1640ecf00475SRafael J. Wysocki * @dev: Device to handle. 1641ecf00475SRafael J. Wysocki */ 1642ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1643ecf00475SRafael J. Wysocki { 1644ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1645ecf00475SRafael J. Wysocki 1646ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.restore_state; 1647ecf00475SRafael J. Wysocki if (cb) 1648ecf00475SRafael J. Wysocki return cb(dev); 1649ecf00475SRafael J. Wysocki 16500b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 16510b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 16520b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 16530b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 16540b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 16550b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 16560b589741SRafael J. Wysocki else 16570b589741SRafael J. Wysocki cb = NULL; 1658ecf00475SRafael J. Wysocki 16590b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 16600b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 16610b589741SRafael J. Wysocki 16620b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1663ecf00475SRafael J. Wysocki } 1664ecf00475SRafael J. Wysocki 16650f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 16660f1d6986SRafael J. Wysocki 1667ecf00475SRafael J. Wysocki /** 1668d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend - Default "device suspend" for PM domians. 1669d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1670d23b9b00SRafael J. Wysocki */ 1671d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev) 1672d23b9b00SRafael J. Wysocki { 1673c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend; 1674d23b9b00SRafael J. Wysocki 1675d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend(dev); 1676d23b9b00SRafael J. Wysocki } 1677d23b9b00SRafael J. Wysocki 1678d23b9b00SRafael J. Wysocki /** 1679d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. 1680d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1681d23b9b00SRafael J. Wysocki */ 1682d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev) 1683d23b9b00SRafael J. Wysocki { 1684c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; 1685d23b9b00SRafael J. Wysocki 16860496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend_late(dev); 1687d23b9b00SRafael J. Wysocki } 1688d23b9b00SRafael J. Wysocki 1689d23b9b00SRafael J. Wysocki /** 1690d23b9b00SRafael J. Wysocki * pm_genpd_default_resume_early - Default "early device resume" for PM domians. 1691d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1692d23b9b00SRafael J. Wysocki */ 1693d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev) 1694d23b9b00SRafael J. Wysocki { 1695c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; 1696d23b9b00SRafael J. Wysocki 16970496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume_early(dev); 1698d23b9b00SRafael J. Wysocki } 1699d23b9b00SRafael J. Wysocki 1700d23b9b00SRafael J. Wysocki /** 1701d23b9b00SRafael J. Wysocki * pm_genpd_default_resume - Default "device resume" for PM domians. 1702d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1703d23b9b00SRafael J. Wysocki */ 1704d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev) 1705d23b9b00SRafael J. Wysocki { 1706c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume; 1707d23b9b00SRafael J. Wysocki 1708d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume(dev); 1709d23b9b00SRafael J. Wysocki } 1710d23b9b00SRafael J. Wysocki 1711d23b9b00SRafael J. Wysocki /** 1712d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze - Default "device freeze" for PM domians. 1713d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1714d23b9b00SRafael J. Wysocki */ 1715d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev) 1716d23b9b00SRafael J. Wysocki { 1717d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; 1718d23b9b00SRafael J. Wysocki 1719d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze(dev); 1720d23b9b00SRafael J. Wysocki } 1721d23b9b00SRafael J. Wysocki 1722d23b9b00SRafael J. Wysocki /** 1723d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. 1724d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1725d23b9b00SRafael J. Wysocki */ 1726d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev) 1727d23b9b00SRafael J. Wysocki { 1728d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; 1729d23b9b00SRafael J. Wysocki 17300496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze_late(dev); 1731d23b9b00SRafael J. Wysocki } 1732d23b9b00SRafael J. Wysocki 1733d23b9b00SRafael J. Wysocki /** 1734d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. 1735d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1736d23b9b00SRafael J. Wysocki */ 1737d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev) 1738d23b9b00SRafael J. Wysocki { 1739d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; 1740d23b9b00SRafael J. Wysocki 17410496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw_early(dev); 1742d23b9b00SRafael J. Wysocki } 1743d23b9b00SRafael J. Wysocki 1744d23b9b00SRafael J. Wysocki /** 1745d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw - Default "device thaw" for PM domians. 1746d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1747d23b9b00SRafael J. Wysocki */ 1748d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev) 1749d23b9b00SRafael J. Wysocki { 1750d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; 1751d23b9b00SRafael J. Wysocki 1752d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw(dev); 1753d23b9b00SRafael J. Wysocki } 1754d23b9b00SRafael J. Wysocki 17550f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 17560f1d6986SRafael J. Wysocki 17570f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend NULL 17580f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late NULL 17590f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early NULL 17600f1d6986SRafael J. Wysocki #define pm_genpd_default_resume NULL 17610f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze NULL 17620f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late NULL 17630f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early NULL 17640f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw NULL 17650f1d6986SRafael J. Wysocki 17660f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */ 17670f1d6986SRafael J. Wysocki 1768d23b9b00SRafael J. Wysocki /** 1769f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1770f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1771f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1772f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1773f721889fSRafael J. Wysocki */ 1774f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1775f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1776f721889fSRafael J. Wysocki { 1777f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1778f721889fSRafael J. Wysocki return; 1779f721889fSRafael J. Wysocki 17805063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 17815063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1782f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1783f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1784f721889fSRafael J. Wysocki genpd->gov = gov; 1785f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1786f721889fSRafael J. Wysocki genpd->in_progress = 0; 1787c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 178817b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 178917b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 1790c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 1791c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 1792596ba34bSRafael J. Wysocki genpd->device_count = 0; 1793221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 17946ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1795f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1796f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1797f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1798596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1799596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 18000496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 1801596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1802596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 18030496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 1804596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1805596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 18060496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 1807596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1808596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 18090496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 1810596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1811d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 18120496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 1813d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1814596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 18150496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 1816d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 1817596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1818ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 1819ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 1820c9914854SRafael J. Wysocki genpd->dev_ops.suspend = pm_genpd_default_suspend; 1821c9914854SRafael J. Wysocki genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late; 1822c9914854SRafael J. Wysocki genpd->dev_ops.resume_early = pm_genpd_default_resume_early; 1823c9914854SRafael J. Wysocki genpd->dev_ops.resume = pm_genpd_default_resume; 1824d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze = pm_genpd_default_freeze; 1825d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; 1826d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; 1827d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw = pm_genpd_default_thaw; 18285125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 18295125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 18305125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 18315125bbf3SRafael J. Wysocki } 1832