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; 34280de3d7fSRafael J. Wysocki bool need_restore = gpd_data->need_restore; 343f721889fSRafael J. Wysocki 34480de3d7fSRafael J. Wysocki gpd_data->need_restore = false; 34517b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 34617b75ecaSRafael J. Wysocki 347d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 34880de3d7fSRafael J. Wysocki if (need_restore) 349ecf00475SRafael J. Wysocki genpd_restore_dev(genpd, dev); 350f721889fSRafael J. Wysocki 35117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 352f721889fSRafael J. Wysocki } 353f721889fSRafael J. Wysocki 354f721889fSRafael J. Wysocki /** 355c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 356c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 357c6d22b37SRafael J. Wysocki * 358c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 359c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 360c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 361c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 362c6d22b37SRafael J. Wysocki */ 363c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 364c6d22b37SRafael J. Wysocki { 36517877eb5SRafael J. Wysocki return genpd->status == GPD_STATE_WAIT_MASTER 3663f241775SRafael J. Wysocki || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 367c6d22b37SRafael J. Wysocki } 368c6d22b37SRafael J. Wysocki 369c6d22b37SRafael J. Wysocki /** 37056375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 37156375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 37256375fd4SRafael J. Wysocki * 37356375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 37456375fd4SRafael J. Wysocki * before. 37556375fd4SRafael J. Wysocki */ 3760bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 37756375fd4SRafael J. Wysocki { 37856375fd4SRafael J. Wysocki if (!work_pending(&genpd->power_off_work)) 37956375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 38056375fd4SRafael J. Wysocki } 38156375fd4SRafael J. Wysocki 38256375fd4SRafael J. Wysocki /** 383f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 384f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 385f721889fSRafael J. Wysocki * 386f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 387f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 388f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 389f721889fSRafael J. Wysocki */ 390f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 39117b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 392f721889fSRafael J. Wysocki { 3934605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 3945063ce15SRafael J. Wysocki struct gpd_link *link; 395f721889fSRafael J. Wysocki unsigned int not_suspended; 396c6d22b37SRafael J. Wysocki int ret = 0; 397f721889fSRafael J. Wysocki 398c6d22b37SRafael J. Wysocki start: 399c6d22b37SRafael J. Wysocki /* 400c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 401c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 4025063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 403c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 4043f241775SRafael J. Wysocki * (4) System suspend is in progress. 405c6d22b37SRafael J. Wysocki */ 4063f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 40717877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 4083f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 409f721889fSRafael J. Wysocki return 0; 410f721889fSRafael J. Wysocki 411c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 412f721889fSRafael J. Wysocki return -EBUSY; 413f721889fSRafael J. Wysocki 414f721889fSRafael J. Wysocki not_suspended = 0; 4154605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 4160aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 4171e78a0c7SRafael J. Wysocki || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on)) 418f721889fSRafael J. Wysocki not_suspended++; 419f721889fSRafael J. Wysocki 420f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 421f721889fSRafael J. Wysocki return -EBUSY; 422f721889fSRafael J. Wysocki 423c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 424c6d22b37SRafael J. Wysocki /* 425c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 426c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 427c6d22b37SRafael J. Wysocki */ 428c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 429c6d22b37SRafael J. Wysocki return 0; 430c6d22b37SRafael J. Wysocki } 431c6d22b37SRafael J. Wysocki 432f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 433f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 434f721889fSRafael J. Wysocki return -EAGAIN; 435f721889fSRafael J. Wysocki } 436f721889fSRafael J. Wysocki 43717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 438c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 43917b75ecaSRafael J. Wysocki 4404605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 4413c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 4424605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 4433f241775SRafael J. Wysocki 4443f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 4453f241775SRafael J. Wysocki goto out; 4463f241775SRafael J. Wysocki 447697a7f37SRafael J. Wysocki if (ret) { 448697a7f37SRafael J. Wysocki genpd_set_active(genpd); 449697a7f37SRafael J. Wysocki goto out; 450697a7f37SRafael J. Wysocki } 451f721889fSRafael J. Wysocki 452c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 453c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 454c6d22b37SRafael J. Wysocki goto start; 455c6d22b37SRafael J. Wysocki } 456c6d22b37SRafael J. Wysocki } 45717b75ecaSRafael J. Wysocki 4583c07cbc4SRafael J. Wysocki if (genpd->power_off) { 4590140d8bdSRafael J. Wysocki ktime_t time_start; 4600140d8bdSRafael J. Wysocki s64 elapsed_ns; 4610140d8bdSRafael J. Wysocki 4623c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 4633c07cbc4SRafael J. Wysocki ret = -EBUSY; 464c6d22b37SRafael J. Wysocki goto out; 465c6d22b37SRafael J. Wysocki } 46617b75ecaSRafael J. Wysocki 4670140d8bdSRafael J. Wysocki time_start = ktime_get(); 4680140d8bdSRafael J. Wysocki 4693c07cbc4SRafael J. Wysocki /* 4705063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 4715063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 4723c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 4733c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 4743c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 4753c07cbc4SRafael J. Wysocki * happen very often). 4763c07cbc4SRafael J. Wysocki */ 477d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 478d2805402SRafael J. Wysocki if (ret == -EBUSY) { 479d2805402SRafael J. Wysocki genpd_set_active(genpd); 480d2805402SRafael J. Wysocki goto out; 481d2805402SRafael J. Wysocki } 4820140d8bdSRafael J. Wysocki 4830140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 484e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_off_latency_ns) { 4850140d8bdSRafael J. Wysocki genpd->power_off_latency_ns = elapsed_ns; 4866ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 487e84b2c20SRafael J. Wysocki if (genpd->name) 488e84b2c20SRafael J. Wysocki pr_warning("%s: Power-off latency exceeded, " 489e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 490e84b2c20SRafael J. Wysocki elapsed_ns); 491e84b2c20SRafael J. Wysocki } 492d2805402SRafael J. Wysocki } 493f721889fSRafael J. Wysocki 49417b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 495221e9b58SRafael J. Wysocki 4965063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 4975063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 4985063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 4995063ce15SRafael J. Wysocki } 50017b75ecaSRafael J. Wysocki 501c6d22b37SRafael J. Wysocki out: 502c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 503c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 504c6d22b37SRafael J. Wysocki return ret; 505f721889fSRafael J. Wysocki } 506f721889fSRafael J. Wysocki 507f721889fSRafael J. Wysocki /** 508f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 509f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 510f721889fSRafael J. Wysocki */ 511f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 512f721889fSRafael J. Wysocki { 513f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 514f721889fSRafael J. Wysocki 515f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 516f721889fSRafael J. Wysocki 51717b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 518f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 51917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 520f721889fSRafael J. Wysocki } 521f721889fSRafael J. Wysocki 522f721889fSRafael J. Wysocki /** 523f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 524f721889fSRafael J. Wysocki * @dev: Device to suspend. 525f721889fSRafael J. Wysocki * 526f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 527f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 528f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 529f721889fSRafael J. Wysocki */ 530f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 531f721889fSRafael J. Wysocki { 532f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 533b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 534d5e4cbfeSRafael J. Wysocki int ret; 535f721889fSRafael J. Wysocki 536f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 537f721889fSRafael J. Wysocki 5385248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5395248051bSRafael J. Wysocki if (IS_ERR(genpd)) 540f721889fSRafael J. Wysocki return -EINVAL; 541f721889fSRafael J. Wysocki 5420aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 5430aa2a221SRafael J. Wysocki 5441e78a0c7SRafael J. Wysocki if (dev_gpd_data(dev)->always_on) 5451e78a0c7SRafael J. Wysocki return -EBUSY; 5461e78a0c7SRafael J. Wysocki 547b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 548b02c999aSRafael J. Wysocki if (stop_ok && !stop_ok(dev)) 549b02c999aSRafael J. Wysocki return -EBUSY; 550b02c999aSRafael J. Wysocki 551d5e4cbfeSRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 552f721889fSRafael J. Wysocki if (ret) 55317b75ecaSRafael J. Wysocki return ret; 55417b75ecaSRafael J. Wysocki 5550aa2a221SRafael J. Wysocki /* 5560aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 5570aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 5580aa2a221SRafael J. Wysocki */ 5590aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 5600aa2a221SRafael J. Wysocki return 0; 5610aa2a221SRafael J. Wysocki 562c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 563f721889fSRafael J. Wysocki genpd->in_progress++; 564f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 565f721889fSRafael J. Wysocki genpd->in_progress--; 566c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 567f721889fSRafael J. Wysocki 568f721889fSRafael J. Wysocki return 0; 569f721889fSRafael J. Wysocki } 570f721889fSRafael J. Wysocki 571f721889fSRafael J. Wysocki /** 572f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 573f721889fSRafael J. Wysocki * @dev: Device to resume. 574f721889fSRafael J. Wysocki * 575f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 576f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 577f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 578f721889fSRafael J. Wysocki */ 579f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 580f721889fSRafael J. Wysocki { 581f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 582c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 583f721889fSRafael J. Wysocki int ret; 584f721889fSRafael J. Wysocki 585f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 586f721889fSRafael J. Wysocki 5875248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5885248051bSRafael J. Wysocki if (IS_ERR(genpd)) 589f721889fSRafael J. Wysocki return -EINVAL; 590f721889fSRafael J. Wysocki 5910aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 5920aa2a221SRafael J. Wysocki 5930aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 5940aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 59580de3d7fSRafael J. Wysocki return genpd_start_dev(genpd, dev); 5960aa2a221SRafael J. Wysocki 597c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 5983f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 5993f241775SRafael J. Wysocki if (ret) { 6003f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 6013f241775SRafael J. Wysocki return ret; 6023f241775SRafael J. Wysocki } 60317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 604c6d22b37SRafael J. Wysocki genpd->resume_count++; 605c6d22b37SRafael J. Wysocki for (;;) { 606c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 607c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 608c6d22b37SRafael J. Wysocki /* 609c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 610c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 611c6d22b37SRafael J. Wysocki * not wait. 612c6d22b37SRafael J. Wysocki */ 613c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 614c6d22b37SRafael J. Wysocki break; 615c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 616c6d22b37SRafael J. Wysocki 617c6d22b37SRafael J. Wysocki schedule(); 618c6d22b37SRafael J. Wysocki 619c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 620c6d22b37SRafael J. Wysocki } 621c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 622cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 623c6d22b37SRafael J. Wysocki genpd->resume_count--; 624c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 62517b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 626c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 62717b75ecaSRafael J. Wysocki 628f721889fSRafael J. Wysocki return 0; 629f721889fSRafael J. Wysocki } 630f721889fSRafael J. Wysocki 63117f2ae7fSRafael J. Wysocki /** 63217f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 63317f2ae7fSRafael J. Wysocki */ 63417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 63517f2ae7fSRafael J. Wysocki { 63617f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 63717f2ae7fSRafael J. Wysocki 63817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 63917f2ae7fSRafael J. Wysocki 64017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 64117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 64217f2ae7fSRafael J. Wysocki 64317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 64417f2ae7fSRafael J. Wysocki } 64517f2ae7fSRafael J. Wysocki 646f721889fSRafael J. Wysocki #else 647f721889fSRafael J. Wysocki 6486ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 6496ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 6506ff7bb0dSRafael J. Wysocki { 6516ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 6526ff7bb0dSRafael J. Wysocki } 6536ff7bb0dSRafael J. Wysocki 654f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 655f721889fSRafael J. Wysocki 656f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 657f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 658f721889fSRafael J. Wysocki 659f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 660f721889fSRafael J. Wysocki 661596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 662596ba34bSRafael J. Wysocki 663d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 664d5e4cbfeSRafael J. Wysocki struct device *dev) 665d5e4cbfeSRafael J. Wysocki { 666d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 667d5e4cbfeSRafael J. Wysocki } 668d5e4cbfeSRafael J. Wysocki 669d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) 670d23b9b00SRafael J. Wysocki { 671d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); 672d23b9b00SRafael J. Wysocki } 673d23b9b00SRafael J. Wysocki 674d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) 675d23b9b00SRafael J. Wysocki { 676d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); 677d23b9b00SRafael J. Wysocki } 678d23b9b00SRafael J. Wysocki 679d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) 680d23b9b00SRafael J. Wysocki { 681d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); 682d23b9b00SRafael J. Wysocki } 683d23b9b00SRafael J. Wysocki 684d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) 685d23b9b00SRafael J. Wysocki { 686d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume, dev); 687d23b9b00SRafael J. Wysocki } 688d23b9b00SRafael J. Wysocki 689d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) 690d23b9b00SRafael J. Wysocki { 691d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); 692d23b9b00SRafael J. Wysocki } 693d23b9b00SRafael J. Wysocki 694d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) 695d23b9b00SRafael J. Wysocki { 696d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); 697d23b9b00SRafael J. Wysocki } 698d23b9b00SRafael J. Wysocki 699d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) 700d23b9b00SRafael J. Wysocki { 701d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); 702d23b9b00SRafael J. Wysocki } 703d23b9b00SRafael J. Wysocki 704d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) 705d23b9b00SRafael J. Wysocki { 706d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); 707d23b9b00SRafael J. Wysocki } 708d23b9b00SRafael J. Wysocki 709596ba34bSRafael J. Wysocki /** 7105063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 711596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 712596ba34bSRafael J. Wysocki * 713596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7145063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 715596ba34bSRafael J. Wysocki * 716596ba34bSRafael J. Wysocki * This function is only called in "noirq" stages of system power transitions, 717596ba34bSRafael J. Wysocki * so it need not acquire locks (all of the "noirq" callbacks are executed 718596ba34bSRafael J. Wysocki * sequentially, so it is guaranteed that it will never run twice in parallel). 719596ba34bSRafael J. Wysocki */ 720596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 721596ba34bSRafael J. Wysocki { 7225063ce15SRafael J. Wysocki struct gpd_link *link; 723596ba34bSRafael J. Wysocki 72417b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 725596ba34bSRafael J. Wysocki return; 726596ba34bSRafael J. Wysocki 727c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 728c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 729596ba34bSRafael J. Wysocki return; 730596ba34bSRafael J. Wysocki 731596ba34bSRafael J. Wysocki if (genpd->power_off) 732596ba34bSRafael J. Wysocki genpd->power_off(genpd); 733596ba34bSRafael J. Wysocki 73417b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7355063ce15SRafael J. Wysocki 7365063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7375063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 7385063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 739596ba34bSRafael J. Wysocki } 740596ba34bSRafael J. Wysocki } 741596ba34bSRafael J. Wysocki 742596ba34bSRafael J. Wysocki /** 7434ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 7444ecd6e65SRafael J. Wysocki * @dev: Device to check. 7454ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 7464ecd6e65SRafael J. Wysocki * 7474ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 7484ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 7494ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 7504ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 7514ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 7524ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 7534ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 7544ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 7554ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 7564ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 7574ecd6e65SRafael J. Wysocki */ 7584ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 7594ecd6e65SRafael J. Wysocki { 7604ecd6e65SRafael J. Wysocki bool active_wakeup; 7614ecd6e65SRafael J. Wysocki 7624ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 7634ecd6e65SRafael J. Wysocki return false; 7644ecd6e65SRafael J. Wysocki 765d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 7664ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 7674ecd6e65SRafael J. Wysocki } 7684ecd6e65SRafael J. Wysocki 7694ecd6e65SRafael J. Wysocki /** 770596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 771596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 772596ba34bSRafael J. Wysocki * 773596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 774596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 775596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 776596ba34bSRafael J. Wysocki * consisting of I/O devices. 777596ba34bSRafael J. Wysocki */ 778596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 779596ba34bSRafael J. Wysocki { 780596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 781b6c10c84SRafael J. Wysocki int ret; 782596ba34bSRafael J. Wysocki 783596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 784596ba34bSRafael J. Wysocki 785596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 786596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 787596ba34bSRafael J. Wysocki return -EINVAL; 788596ba34bSRafael J. Wysocki 78917b75ecaSRafael J. Wysocki /* 79017b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 79117b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 79217b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 79317b75ecaSRafael J. Wysocki */ 79417b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 79517b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 79617b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 79717b75ecaSRafael J. Wysocki 79817b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 79917b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 80017b75ecaSRafael J. Wysocki return -EBUSY; 80117b75ecaSRafael J. Wysocki } 80217b75ecaSRafael J. Wysocki 8034ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8044ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8054ecd6e65SRafael J. Wysocki 80617b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 807596ba34bSRafael J. Wysocki 80865533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 80965533bbfSRafael J. Wysocki genpd->suspended_count = 0; 81017b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 81165533bbfSRafael J. Wysocki } 81217b75ecaSRafael J. Wysocki 81317b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 814596ba34bSRafael J. Wysocki 815596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 81617b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 817596ba34bSRafael J. Wysocki return 0; 818596ba34bSRafael J. Wysocki } 819596ba34bSRafael J. Wysocki 820596ba34bSRafael J. Wysocki /* 82117b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 82217b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 823d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 82417b75ecaSRafael J. Wysocki * to make it operational. 825596ba34bSRafael J. Wysocki */ 82617b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 827596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 828596ba34bSRafael J. Wysocki 829b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 830b6c10c84SRafael J. Wysocki if (ret) { 831b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 832b6c10c84SRafael J. Wysocki 833b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 834b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 835b6c10c84SRafael J. Wysocki 836b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 83717b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 838b6c10c84SRafael J. Wysocki } 83917b75ecaSRafael J. Wysocki 84017b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 841b6c10c84SRafael J. Wysocki return ret; 842596ba34bSRafael J. Wysocki } 843596ba34bSRafael J. Wysocki 844596ba34bSRafael J. Wysocki /** 845596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 846596ba34bSRafael J. Wysocki * @dev: Device to suspend. 847596ba34bSRafael J. Wysocki * 848596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 849596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 850596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 851596ba34bSRafael J. Wysocki */ 852596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 853596ba34bSRafael J. Wysocki { 854596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 855596ba34bSRafael J. Wysocki 856596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 857596ba34bSRafael J. Wysocki 858596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 859596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 860596ba34bSRafael J. Wysocki return -EINVAL; 861596ba34bSRafael J. Wysocki 862d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); 863596ba34bSRafael J. Wysocki } 864596ba34bSRafael J. Wysocki 865596ba34bSRafael J. Wysocki /** 8660496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 867596ba34bSRafael J. Wysocki * @dev: Device to suspend. 868596ba34bSRafael J. Wysocki * 869596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 870596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 871596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 872596ba34bSRafael J. Wysocki */ 8730496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 874596ba34bSRafael J. Wysocki { 875596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 876596ba34bSRafael J. Wysocki 877596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 878596ba34bSRafael J. Wysocki 879596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 880596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 881596ba34bSRafael J. Wysocki return -EINVAL; 882596ba34bSRafael J. Wysocki 8830496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); 8840496c8aeSRafael J. Wysocki } 885596ba34bSRafael J. Wysocki 8860496c8aeSRafael J. Wysocki /** 8870496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 8880496c8aeSRafael J. Wysocki * @dev: Device to suspend. 8890496c8aeSRafael J. Wysocki * 8900496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 8910496c8aeSRafael J. Wysocki * been stopped. 8920496c8aeSRafael J. Wysocki */ 8930496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 8940496c8aeSRafael J. Wysocki { 8950496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 896596ba34bSRafael J. Wysocki 8970496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 8980496c8aeSRafael J. Wysocki 8990496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9000496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9010496c8aeSRafael J. Wysocki return -EINVAL; 9020496c8aeSRafael J. Wysocki 9031e78a0c7SRafael J. Wysocki if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on 9040496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 905d4f2d87aSRafael J. Wysocki return 0; 906d4f2d87aSRafael J. Wysocki 907d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 908596ba34bSRafael J. Wysocki 909596ba34bSRafael J. Wysocki /* 910596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 911596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 912596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 913596ba34bSRafael J. Wysocki */ 914596ba34bSRafael J. Wysocki genpd->suspended_count++; 915596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 916596ba34bSRafael J. Wysocki 917596ba34bSRafael J. Wysocki return 0; 918596ba34bSRafael J. Wysocki } 919596ba34bSRafael J. Wysocki 920596ba34bSRafael J. Wysocki /** 9210496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 922596ba34bSRafael J. Wysocki * @dev: Device to resume. 923596ba34bSRafael J. Wysocki * 9240496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 925596ba34bSRafael J. Wysocki */ 926596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 927596ba34bSRafael J. Wysocki { 928596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 929596ba34bSRafael J. Wysocki 930596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 931596ba34bSRafael J. Wysocki 932596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 933596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 934596ba34bSRafael J. Wysocki return -EINVAL; 935596ba34bSRafael J. Wysocki 9361e78a0c7SRafael J. Wysocki if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on 937cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 938596ba34bSRafael J. Wysocki return 0; 939596ba34bSRafael J. Wysocki 940596ba34bSRafael J. Wysocki /* 941596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 942596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 943596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 944596ba34bSRafael J. Wysocki */ 945596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 946596ba34bSRafael J. Wysocki genpd->suspended_count--; 947596ba34bSRafael J. Wysocki 9480496c8aeSRafael J. Wysocki return genpd_start_dev(genpd, dev); 949596ba34bSRafael J. Wysocki } 950596ba34bSRafael J. Wysocki 951596ba34bSRafael J. Wysocki /** 9520496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 9530496c8aeSRafael J. Wysocki * @dev: Device to resume. 9540496c8aeSRafael J. Wysocki * 9550496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 9560496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 9570496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 9580496c8aeSRafael J. Wysocki * devices. 9590496c8aeSRafael J. Wysocki */ 9600496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 9610496c8aeSRafael J. Wysocki { 9620496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 9630496c8aeSRafael J. Wysocki 9640496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9650496c8aeSRafael J. Wysocki 9660496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9670496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9680496c8aeSRafael J. Wysocki return -EINVAL; 9690496c8aeSRafael J. Wysocki 9700496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); 9710496c8aeSRafael J. Wysocki } 9720496c8aeSRafael J. Wysocki 9730496c8aeSRafael J. Wysocki /** 9740496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 975596ba34bSRafael J. Wysocki * @dev: Device to resume. 976596ba34bSRafael J. Wysocki * 977596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 978596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 979596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 980596ba34bSRafael J. Wysocki */ 981596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev) 982596ba34bSRafael J. Wysocki { 983596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 984596ba34bSRafael J. Wysocki 985596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 986596ba34bSRafael J. Wysocki 987596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 988596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 989596ba34bSRafael J. Wysocki return -EINVAL; 990596ba34bSRafael J. Wysocki 991d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); 992596ba34bSRafael J. Wysocki } 993596ba34bSRafael J. Wysocki 994596ba34bSRafael J. Wysocki /** 9950496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 996596ba34bSRafael J. Wysocki * @dev: Device to freeze. 997596ba34bSRafael J. Wysocki * 998596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 999596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1000596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1001596ba34bSRafael J. Wysocki */ 1002596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 1003596ba34bSRafael J. Wysocki { 1004596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1005596ba34bSRafael J. Wysocki 1006596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1007596ba34bSRafael J. Wysocki 1008596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1009596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1010596ba34bSRafael J. Wysocki return -EINVAL; 1011596ba34bSRafael J. Wysocki 1012d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); 1013596ba34bSRafael J. Wysocki } 1014596ba34bSRafael J. Wysocki 1015596ba34bSRafael J. Wysocki /** 10160496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 10170496c8aeSRafael J. Wysocki * @dev: Device to freeze. 10180496c8aeSRafael J. Wysocki * 10190496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 10200496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10210496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10220496c8aeSRafael J. Wysocki * devices. 10230496c8aeSRafael J. Wysocki */ 10240496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 10250496c8aeSRafael J. Wysocki { 10260496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10270496c8aeSRafael J. Wysocki 10280496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10290496c8aeSRafael J. Wysocki 10300496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10310496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10320496c8aeSRafael J. Wysocki return -EINVAL; 10330496c8aeSRafael J. Wysocki 10340496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); 10350496c8aeSRafael J. Wysocki } 10360496c8aeSRafael J. Wysocki 10370496c8aeSRafael J. Wysocki /** 10380496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1039596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1040596ba34bSRafael J. Wysocki * 1041596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1042596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1043596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1044596ba34bSRafael J. Wysocki * devices. 1045596ba34bSRafael J. Wysocki */ 1046596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1047596ba34bSRafael J. Wysocki { 1048596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1049596ba34bSRafael J. Wysocki 1050596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1051596ba34bSRafael J. Wysocki 1052596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1053596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1054596ba34bSRafael J. Wysocki return -EINVAL; 1055596ba34bSRafael J. Wysocki 10561e78a0c7SRafael J. Wysocki return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? 10571e78a0c7SRafael J. Wysocki 0 : genpd_stop_dev(genpd, dev); 1058596ba34bSRafael J. Wysocki } 1059596ba34bSRafael J. Wysocki 1060596ba34bSRafael J. Wysocki /** 10610496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1062596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1063596ba34bSRafael J. Wysocki * 10640496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 10650496c8aeSRafael J. Wysocki * before the system transition. 1066596ba34bSRafael J. Wysocki */ 1067596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1068596ba34bSRafael J. Wysocki { 1069596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1070596ba34bSRafael J. Wysocki 1071596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1072596ba34bSRafael J. Wysocki 1073596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1074596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1075596ba34bSRafael J. Wysocki return -EINVAL; 1076596ba34bSRafael J. Wysocki 10771e78a0c7SRafael J. Wysocki return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? 10781e78a0c7SRafael J. Wysocki 0 : genpd_start_dev(genpd, dev); 10790496c8aeSRafael J. Wysocki } 1080596ba34bSRafael J. Wysocki 10810496c8aeSRafael J. Wysocki /** 10820496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 10830496c8aeSRafael J. Wysocki * @dev: Device to thaw. 10840496c8aeSRafael J. Wysocki * 10850496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 10860496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10870496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10880496c8aeSRafael J. Wysocki * devices. 10890496c8aeSRafael J. Wysocki */ 10900496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 10910496c8aeSRafael J. Wysocki { 10920496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1093596ba34bSRafael J. Wysocki 10940496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10950496c8aeSRafael J. Wysocki 10960496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10970496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10980496c8aeSRafael J. Wysocki return -EINVAL; 10990496c8aeSRafael J. Wysocki 11000496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); 1101596ba34bSRafael J. Wysocki } 1102596ba34bSRafael J. Wysocki 1103596ba34bSRafael J. Wysocki /** 1104596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1105596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1106596ba34bSRafael J. Wysocki * 1107596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1108596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1109596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1110596ba34bSRafael J. Wysocki */ 1111596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1112596ba34bSRafael J. Wysocki { 1113596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1114596ba34bSRafael J. Wysocki 1115596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1116596ba34bSRafael J. Wysocki 1117596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1118596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1119596ba34bSRafael J. Wysocki return -EINVAL; 1120596ba34bSRafael J. Wysocki 1121d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); 1122596ba34bSRafael J. Wysocki } 1123596ba34bSRafael J. Wysocki 1124596ba34bSRafael J. Wysocki /** 11250496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1126596ba34bSRafael J. Wysocki * @dev: Device to resume. 1127596ba34bSRafael J. Wysocki * 11280496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 11290496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1130596ba34bSRafael J. Wysocki */ 1131596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1132596ba34bSRafael J. Wysocki { 1133596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1134596ba34bSRafael J. Wysocki 1135596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1136596ba34bSRafael J. Wysocki 1137596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1138596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1139596ba34bSRafael J. Wysocki return -EINVAL; 1140596ba34bSRafael J. Wysocki 1141596ba34bSRafael J. Wysocki /* 1142596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1143596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1144596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 114565533bbfSRafael J. Wysocki * 114665533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 114765533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 114865533bbfSRafael J. Wysocki */ 114965533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 115065533bbfSRafael J. Wysocki /* 115165533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 115265533bbfSRafael J. Wysocki * so make it appear as powered off to pm_genpd_poweron(), so 115365533bbfSRafael J. Wysocki * that it tries to power it on in case it was really off. 1154596ba34bSRafael J. Wysocki */ 115517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1156596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1157596ba34bSRafael J. Wysocki /* 115865533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 115965533bbfSRafael J. Wysocki * sure it will be off going forward. 1160596ba34bSRafael J. Wysocki */ 1161596ba34bSRafael J. Wysocki if (genpd->power_off) 1162596ba34bSRafael J. Wysocki genpd->power_off(genpd); 116365533bbfSRafael J. Wysocki 1164596ba34bSRafael J. Wysocki return 0; 1165596ba34bSRafael J. Wysocki } 116665533bbfSRafael J. Wysocki } 1167596ba34bSRafael J. Wysocki 116818dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 116918dd2eceSRafael J. Wysocki return 0; 117018dd2eceSRafael J. Wysocki 1171596ba34bSRafael J. Wysocki pm_genpd_poweron(genpd); 1172596ba34bSRafael J. Wysocki 11731e78a0c7SRafael J. Wysocki return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev); 1174596ba34bSRafael J. Wysocki } 1175596ba34bSRafael J. Wysocki 1176596ba34bSRafael J. Wysocki /** 1177596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1178596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1179596ba34bSRafael J. Wysocki * 1180596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1181596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1182596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1183596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1184596ba34bSRafael J. Wysocki */ 1185596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1186596ba34bSRafael J. Wysocki { 1187596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1188596ba34bSRafael J. Wysocki bool run_complete; 1189596ba34bSRafael J. Wysocki 1190596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1191596ba34bSRafael J. Wysocki 1192596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1193596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1194596ba34bSRafael J. Wysocki return; 1195596ba34bSRafael J. Wysocki 1196596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1197596ba34bSRafael J. Wysocki 1198596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1199596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1200596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1201596ba34bSRafael J. Wysocki 1202596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1203596ba34bSRafael J. Wysocki 1204596ba34bSRafael J. Wysocki if (run_complete) { 1205596ba34bSRafael J. Wysocki pm_generic_complete(dev); 12066f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1207596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 12086f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 1209596ba34bSRafael J. Wysocki } 1210596ba34bSRafael J. Wysocki } 1211596ba34bSRafael J. Wysocki 1212596ba34bSRafael J. Wysocki #else 1213596ba34bSRafael J. Wysocki 1214596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1215596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 12160496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1217596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 12180496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1219596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1220596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1221596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 12220496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1223596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 12240496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1225596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1226596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1227596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1228596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1229596ba34bSRafael J. Wysocki 1230596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1231596ba34bSRafael J. Wysocki 1232f721889fSRafael J. Wysocki /** 1233b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1234f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1235f721889fSRafael J. Wysocki * @dev: Device to be added. 1236b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1237f721889fSRafael J. Wysocki */ 1238b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1239b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1240f721889fSRafael J. Wysocki { 1241cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 12424605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1243f721889fSRafael J. Wysocki int ret = 0; 1244f721889fSRafael J. Wysocki 1245f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1246f721889fSRafael J. Wysocki 1247f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1248f721889fSRafael J. Wysocki return -EINVAL; 1249f721889fSRafael J. Wysocki 12506ff7bb0dSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 12516ff7bb0dSRafael J. Wysocki if (!gpd_data) 12526ff7bb0dSRafael J. Wysocki return -ENOMEM; 12536ff7bb0dSRafael J. Wysocki 12546ff7bb0dSRafael J. Wysocki mutex_init(&gpd_data->lock); 12556ff7bb0dSRafael J. Wysocki gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 12566ff7bb0dSRafael J. Wysocki dev_pm_qos_add_notifier(dev, &gpd_data->nb); 12576ff7bb0dSRafael J. Wysocki 125817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1259f721889fSRafael J. Wysocki 1260596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1261596ba34bSRafael J. Wysocki ret = -EAGAIN; 1262596ba34bSRafael J. Wysocki goto out; 1263596ba34bSRafael J. Wysocki } 1264596ba34bSRafael J. Wysocki 12654605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 12664605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1267f721889fSRafael J. Wysocki ret = -EINVAL; 1268f721889fSRafael J. Wysocki goto out; 1269f721889fSRafael J. Wysocki } 1270f721889fSRafael J. Wysocki 1271596ba34bSRafael J. Wysocki genpd->device_count++; 12726ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1273f721889fSRafael J. Wysocki 12744605ab65SRafael J. Wysocki dev_pm_get_subsys_data(dev); 12756ff7bb0dSRafael J. Wysocki 12766ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 12776ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 12786ff7bb0dSRafael J. Wysocki dev->pm_domain = &genpd->domain; 1279cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 1280cd0ea672SRafael J. Wysocki gpd_data->base.dev = dev; 1281cd0ea672SRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 1282ca1d72f0SRafael J. Wysocki gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 1283b02c999aSRafael J. Wysocki if (td) 1284b02c999aSRafael J. Wysocki gpd_data->td = *td; 1285f721889fSRafael J. Wysocki 12866ff7bb0dSRafael J. Wysocki gpd_data->td.constraint_changed = true; 12876ff7bb0dSRafael J. Wysocki gpd_data->td.effective_constraint_ns = -1; 12886ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 12896ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 12906ff7bb0dSRafael J. Wysocki 12916ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 12926ff7bb0dSRafael J. Wysocki 12936ff7bb0dSRafael J. Wysocki return 0; 12946ff7bb0dSRafael J. Wysocki 1295f721889fSRafael J. Wysocki out: 129617b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1297f721889fSRafael J. Wysocki 12986ff7bb0dSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 12996ff7bb0dSRafael J. Wysocki kfree(gpd_data); 1300f721889fSRafael J. Wysocki return ret; 1301f721889fSRafael J. Wysocki } 1302f721889fSRafael J. Wysocki 1303f721889fSRafael J. Wysocki /** 1304c8aa130bSThomas Abraham * __pm_genpd_of_add_device - Add a device to an I/O PM domain. 1305c8aa130bSThomas Abraham * @genpd_node: Device tree node pointer representing a PM domain to which the 1306c8aa130bSThomas Abraham * the device is added to. 1307c8aa130bSThomas Abraham * @dev: Device to be added. 1308c8aa130bSThomas Abraham * @td: Set of PM QoS timing parameters to attach to the device. 1309c8aa130bSThomas Abraham */ 1310c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, 1311c8aa130bSThomas Abraham struct gpd_timing_data *td) 1312c8aa130bSThomas Abraham { 1313c8aa130bSThomas Abraham struct generic_pm_domain *genpd = NULL, *gpd; 1314c8aa130bSThomas Abraham 1315c8aa130bSThomas Abraham dev_dbg(dev, "%s()\n", __func__); 1316c8aa130bSThomas Abraham 1317c8aa130bSThomas Abraham if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) 1318c8aa130bSThomas Abraham return -EINVAL; 1319c8aa130bSThomas Abraham 1320c8aa130bSThomas Abraham mutex_lock(&gpd_list_lock); 1321c8aa130bSThomas Abraham list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1322c8aa130bSThomas Abraham if (gpd->of_node == genpd_node) { 1323c8aa130bSThomas Abraham genpd = gpd; 1324c8aa130bSThomas Abraham break; 1325c8aa130bSThomas Abraham } 1326c8aa130bSThomas Abraham } 1327c8aa130bSThomas Abraham mutex_unlock(&gpd_list_lock); 1328c8aa130bSThomas Abraham 1329c8aa130bSThomas Abraham if (!genpd) 1330c8aa130bSThomas Abraham return -EINVAL; 1331c8aa130bSThomas Abraham 1332c8aa130bSThomas Abraham return __pm_genpd_add_device(genpd, dev, td); 1333c8aa130bSThomas Abraham } 1334c8aa130bSThomas Abraham 1335c8aa130bSThomas Abraham /** 1336f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1337f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1338f721889fSRafael J. Wysocki * @dev: Device to be removed. 1339f721889fSRafael J. Wysocki */ 1340f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1341f721889fSRafael J. Wysocki struct device *dev) 1342f721889fSRafael J. Wysocki { 13436ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13444605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1345efa69025SRafael J. Wysocki int ret = 0; 1346f721889fSRafael J. Wysocki 1347f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1348f721889fSRafael J. Wysocki 1349efa69025SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) 1350efa69025SRafael J. Wysocki || IS_ERR_OR_NULL(dev->pm_domain) 1351efa69025SRafael J. Wysocki || pd_to_genpd(dev->pm_domain) != genpd) 1352f721889fSRafael J. Wysocki return -EINVAL; 1353f721889fSRafael J. Wysocki 135417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1355f721889fSRafael J. Wysocki 1356596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1357596ba34bSRafael J. Wysocki ret = -EAGAIN; 1358596ba34bSRafael J. Wysocki goto out; 1359596ba34bSRafael J. Wysocki } 1360596ba34bSRafael J. Wysocki 13616ff7bb0dSRafael J. Wysocki genpd->device_count--; 13626ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 13636ff7bb0dSRafael J. Wysocki 13646ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1365f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1366efa69025SRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1367efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 1368efa69025SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 13696ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1370f721889fSRafael J. Wysocki 13716ff7bb0dSRafael J. Wysocki gpd_data = to_gpd_data(pdd); 13726ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 13736ff7bb0dSRafael J. Wysocki pdd->dev = NULL; 13746ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 13756ff7bb0dSRafael J. Wysocki 13766ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 13776ff7bb0dSRafael J. Wysocki 13786ff7bb0dSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13796ff7bb0dSRafael J. Wysocki kfree(gpd_data); 13806ff7bb0dSRafael J. Wysocki dev_pm_put_subsys_data(dev); 13816ff7bb0dSRafael J. Wysocki return 0; 1382f721889fSRafael J. Wysocki 1383596ba34bSRafael J. Wysocki out: 138417b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1385f721889fSRafael J. Wysocki 1386f721889fSRafael J. Wysocki return ret; 1387f721889fSRafael J. Wysocki } 1388f721889fSRafael J. Wysocki 1389f721889fSRafael J. Wysocki /** 13901e78a0c7SRafael J. Wysocki * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device. 13911e78a0c7SRafael J. Wysocki * @dev: Device to set/unset the flag for. 13921e78a0c7SRafael J. Wysocki * @val: The new value of the device's "always on" flag. 13931e78a0c7SRafael J. Wysocki */ 13941e78a0c7SRafael J. Wysocki void pm_genpd_dev_always_on(struct device *dev, bool val) 13951e78a0c7SRafael J. Wysocki { 13961e78a0c7SRafael J. Wysocki struct pm_subsys_data *psd; 13971e78a0c7SRafael J. Wysocki unsigned long flags; 13981e78a0c7SRafael J. Wysocki 13991e78a0c7SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 14001e78a0c7SRafael J. Wysocki 14011e78a0c7SRafael J. Wysocki psd = dev_to_psd(dev); 14021e78a0c7SRafael J. Wysocki if (psd && psd->domain_data) 14031e78a0c7SRafael J. Wysocki to_gpd_data(psd->domain_data)->always_on = val; 14041e78a0c7SRafael J. Wysocki 14051e78a0c7SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 14061e78a0c7SRafael J. Wysocki } 14071e78a0c7SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); 14081e78a0c7SRafael J. Wysocki 14091e78a0c7SRafael J. Wysocki /** 1410ca1d72f0SRafael J. Wysocki * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. 1411ca1d72f0SRafael J. Wysocki * @dev: Device to set/unset the flag for. 1412ca1d72f0SRafael J. Wysocki * @val: The new value of the device's "need restore" flag. 1413ca1d72f0SRafael J. Wysocki */ 1414ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val) 1415ca1d72f0SRafael J. Wysocki { 1416ca1d72f0SRafael J. Wysocki struct pm_subsys_data *psd; 1417ca1d72f0SRafael J. Wysocki unsigned long flags; 1418ca1d72f0SRafael J. Wysocki 1419ca1d72f0SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 1420ca1d72f0SRafael J. Wysocki 1421ca1d72f0SRafael J. Wysocki psd = dev_to_psd(dev); 1422ca1d72f0SRafael J. Wysocki if (psd && psd->domain_data) 1423ca1d72f0SRafael J. Wysocki to_gpd_data(psd->domain_data)->need_restore = val; 1424ca1d72f0SRafael J. Wysocki 1425ca1d72f0SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 1426ca1d72f0SRafael J. Wysocki } 1427ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); 1428ca1d72f0SRafael J. Wysocki 1429ca1d72f0SRafael J. Wysocki /** 1430f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1431f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1432bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1433f721889fSRafael J. Wysocki */ 1434f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1435bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1436f721889fSRafael J. Wysocki { 14375063ce15SRafael J. Wysocki struct gpd_link *link; 1438f721889fSRafael J. Wysocki int ret = 0; 1439f721889fSRafael J. Wysocki 1440bc0403ffSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1441f721889fSRafael J. Wysocki return -EINVAL; 1442f721889fSRafael J. Wysocki 144317b75ecaSRafael J. Wysocki start: 144417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1445bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1446f721889fSRafael J. Wysocki 1447bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1448bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1449bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 145017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 145117b75ecaSRafael J. Wysocki goto start; 145217b75ecaSRafael J. Wysocki } 145317b75ecaSRafael J. Wysocki 145417b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1455bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1456f721889fSRafael J. Wysocki ret = -EINVAL; 1457f721889fSRafael J. Wysocki goto out; 1458f721889fSRafael J. Wysocki } 1459f721889fSRafael J. Wysocki 14604fcac10dSHuang Ying list_for_each_entry(link, &genpd->master_links, master_node) { 1461bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1462f721889fSRafael J. Wysocki ret = -EINVAL; 1463f721889fSRafael J. Wysocki goto out; 1464f721889fSRafael J. Wysocki } 1465f721889fSRafael J. Wysocki } 1466f721889fSRafael J. Wysocki 14675063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 14685063ce15SRafael J. Wysocki if (!link) { 14695063ce15SRafael J. Wysocki ret = -ENOMEM; 14705063ce15SRafael J. Wysocki goto out; 14715063ce15SRafael J. Wysocki } 14725063ce15SRafael J. Wysocki link->master = genpd; 14735063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1474bc0403ffSRafael J. Wysocki link->slave = subdomain; 1475bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1476bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1477c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1478f721889fSRafael J. Wysocki 1479f721889fSRafael J. Wysocki out: 1480bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 148117b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1482f721889fSRafael J. Wysocki 1483f721889fSRafael J. Wysocki return ret; 1484f721889fSRafael J. Wysocki } 1485f721889fSRafael J. Wysocki 1486f721889fSRafael J. Wysocki /** 1487f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1488f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 14895063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1490f721889fSRafael J. Wysocki */ 1491f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 14925063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1493f721889fSRafael J. Wysocki { 14945063ce15SRafael J. Wysocki struct gpd_link *link; 1495f721889fSRafael J. Wysocki int ret = -EINVAL; 1496f721889fSRafael J. Wysocki 14975063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1498f721889fSRafael J. Wysocki return -EINVAL; 1499f721889fSRafael J. Wysocki 150017b75ecaSRafael J. Wysocki start: 150117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1502f721889fSRafael J. Wysocki 15035063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 15045063ce15SRafael J. Wysocki if (link->slave != subdomain) 1505f721889fSRafael J. Wysocki continue; 1506f721889fSRafael J. Wysocki 1507f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1508f721889fSRafael J. Wysocki 150917b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 151017b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 151117b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 151217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 151317b75ecaSRafael J. Wysocki goto start; 151417b75ecaSRafael J. Wysocki } 151517b75ecaSRafael J. Wysocki 15165063ce15SRafael J. Wysocki list_del(&link->master_node); 15175063ce15SRafael J. Wysocki list_del(&link->slave_node); 15185063ce15SRafael J. Wysocki kfree(link); 151917b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1520f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1521f721889fSRafael J. Wysocki 1522f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1523f721889fSRafael J. Wysocki 1524f721889fSRafael J. Wysocki ret = 0; 1525f721889fSRafael J. Wysocki break; 1526f721889fSRafael J. Wysocki } 1527f721889fSRafael J. Wysocki 152817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1529f721889fSRafael J. Wysocki 1530f721889fSRafael J. Wysocki return ret; 1531f721889fSRafael J. Wysocki } 1532f721889fSRafael J. Wysocki 1533f721889fSRafael J. Wysocki /** 1534d5e4cbfeSRafael J. Wysocki * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. 1535d5e4cbfeSRafael J. Wysocki * @dev: Device to add the callbacks to. 1536d5e4cbfeSRafael J. Wysocki * @ops: Set of callbacks to add. 1537b02c999aSRafael J. Wysocki * @td: Timing data to add to the device along with the callbacks (optional). 1538d5e4cbfeSRafael J. Wysocki */ 1539b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, 1540b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1541d5e4cbfeSRafael J. Wysocki { 1542d5e4cbfeSRafael J. Wysocki struct pm_domain_data *pdd; 1543d5e4cbfeSRafael J. Wysocki int ret = 0; 1544d5e4cbfeSRafael J. Wysocki 1545d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data && ops)) 1546d5e4cbfeSRafael J. Wysocki return -EINVAL; 1547d5e4cbfeSRafael J. Wysocki 1548d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1549d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1550d5e4cbfeSRafael J. Wysocki 1551d5e4cbfeSRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1552d5e4cbfeSRafael J. Wysocki if (pdd) { 1553d5e4cbfeSRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1554d5e4cbfeSRafael J. Wysocki 1555d5e4cbfeSRafael J. Wysocki gpd_data->ops = *ops; 1556b02c999aSRafael J. Wysocki if (td) 1557b02c999aSRafael J. Wysocki gpd_data->td = *td; 1558d5e4cbfeSRafael J. Wysocki } else { 1559d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1560d5e4cbfeSRafael J. Wysocki } 1561d5e4cbfeSRafael J. Wysocki 1562d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1563d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1564d5e4cbfeSRafael J. Wysocki 1565d5e4cbfeSRafael J. Wysocki return ret; 1566d5e4cbfeSRafael J. Wysocki } 1567d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); 1568d5e4cbfeSRafael J. Wysocki 1569d5e4cbfeSRafael J. Wysocki /** 1570b02c999aSRafael J. Wysocki * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. 1571d5e4cbfeSRafael J. Wysocki * @dev: Device to remove the callbacks from. 1572b02c999aSRafael J. Wysocki * @clear_td: If set, clear the device's timing data too. 1573d5e4cbfeSRafael J. Wysocki */ 1574b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) 1575d5e4cbfeSRafael J. Wysocki { 1576d5e4cbfeSRafael J. Wysocki struct pm_domain_data *pdd; 1577d5e4cbfeSRafael J. Wysocki int ret = 0; 1578d5e4cbfeSRafael J. Wysocki 1579d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data)) 1580d5e4cbfeSRafael J. Wysocki return -EINVAL; 1581d5e4cbfeSRafael J. Wysocki 1582d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1583d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1584d5e4cbfeSRafael J. Wysocki 1585d5e4cbfeSRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1586d5e4cbfeSRafael J. Wysocki if (pdd) { 1587d5e4cbfeSRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 1588d5e4cbfeSRafael J. Wysocki 1589d5e4cbfeSRafael J. Wysocki gpd_data->ops = (struct gpd_dev_ops){ 0 }; 1590b02c999aSRafael J. Wysocki if (clear_td) 1591b02c999aSRafael J. Wysocki gpd_data->td = (struct gpd_timing_data){ 0 }; 1592d5e4cbfeSRafael J. Wysocki } else { 1593d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1594d5e4cbfeSRafael J. Wysocki } 1595d5e4cbfeSRafael J. Wysocki 1596d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1597d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1598d5e4cbfeSRafael J. Wysocki 1599d5e4cbfeSRafael J. Wysocki return ret; 1600d5e4cbfeSRafael J. Wysocki } 1601b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); 1602d5e4cbfeSRafael J. Wysocki 1603d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1604d23b9b00SRafael J. Wysocki 1605d5e4cbfeSRafael J. Wysocki /** 1606ecf00475SRafael J. Wysocki * pm_genpd_default_save_state - Default "save device state" for PM domians. 1607ecf00475SRafael J. Wysocki * @dev: Device to handle. 1608ecf00475SRafael J. Wysocki */ 1609ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1610ecf00475SRafael J. Wysocki { 1611ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1612ecf00475SRafael J. Wysocki 1613ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.save_state; 1614ecf00475SRafael J. Wysocki if (cb) 1615ecf00475SRafael J. Wysocki return cb(dev); 1616ecf00475SRafael J. Wysocki 16170b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 16180b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 16190b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 16200b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 16210b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 16220b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 16230b589741SRafael J. Wysocki else 16240b589741SRafael J. Wysocki cb = NULL; 1625ecf00475SRafael J. Wysocki 16260b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 16270b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 16280b589741SRafael J. Wysocki 16290b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1630ecf00475SRafael J. Wysocki } 1631ecf00475SRafael J. Wysocki 1632ecf00475SRafael J. Wysocki /** 1633ecf00475SRafael J. Wysocki * pm_genpd_default_restore_state - Default PM domians "restore device state". 1634ecf00475SRafael J. Wysocki * @dev: Device to handle. 1635ecf00475SRafael J. Wysocki */ 1636ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1637ecf00475SRafael J. Wysocki { 1638ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1639ecf00475SRafael J. Wysocki 1640ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.restore_state; 1641ecf00475SRafael J. Wysocki if (cb) 1642ecf00475SRafael J. Wysocki return cb(dev); 1643ecf00475SRafael J. Wysocki 16440b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 16450b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 16460b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 16470b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 16480b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 16490b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 16500b589741SRafael J. Wysocki else 16510b589741SRafael J. Wysocki cb = NULL; 1652ecf00475SRafael J. Wysocki 16530b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 16540b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 16550b589741SRafael J. Wysocki 16560b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1657ecf00475SRafael J. Wysocki } 1658ecf00475SRafael J. Wysocki 16590f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 16600f1d6986SRafael J. Wysocki 1661ecf00475SRafael J. Wysocki /** 1662d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend - Default "device suspend" for PM domians. 1663d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1664d23b9b00SRafael J. Wysocki */ 1665d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev) 1666d23b9b00SRafael J. Wysocki { 1667c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend; 1668d23b9b00SRafael J. Wysocki 1669d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend(dev); 1670d23b9b00SRafael J. Wysocki } 1671d23b9b00SRafael J. Wysocki 1672d23b9b00SRafael J. Wysocki /** 1673d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. 1674d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1675d23b9b00SRafael J. Wysocki */ 1676d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev) 1677d23b9b00SRafael J. Wysocki { 1678c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; 1679d23b9b00SRafael J. Wysocki 16800496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend_late(dev); 1681d23b9b00SRafael J. Wysocki } 1682d23b9b00SRafael J. Wysocki 1683d23b9b00SRafael J. Wysocki /** 1684d23b9b00SRafael J. Wysocki * pm_genpd_default_resume_early - Default "early device resume" for PM domians. 1685d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1686d23b9b00SRafael J. Wysocki */ 1687d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev) 1688d23b9b00SRafael J. Wysocki { 1689c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; 1690d23b9b00SRafael J. Wysocki 16910496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume_early(dev); 1692d23b9b00SRafael J. Wysocki } 1693d23b9b00SRafael J. Wysocki 1694d23b9b00SRafael J. Wysocki /** 1695d23b9b00SRafael J. Wysocki * pm_genpd_default_resume - Default "device resume" for PM domians. 1696d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1697d23b9b00SRafael J. Wysocki */ 1698d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev) 1699d23b9b00SRafael J. Wysocki { 1700c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume; 1701d23b9b00SRafael J. Wysocki 1702d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume(dev); 1703d23b9b00SRafael J. Wysocki } 1704d23b9b00SRafael J. Wysocki 1705d23b9b00SRafael J. Wysocki /** 1706d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze - Default "device freeze" for PM domians. 1707d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1708d23b9b00SRafael J. Wysocki */ 1709d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev) 1710d23b9b00SRafael J. Wysocki { 1711d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; 1712d23b9b00SRafael J. Wysocki 1713d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze(dev); 1714d23b9b00SRafael J. Wysocki } 1715d23b9b00SRafael J. Wysocki 1716d23b9b00SRafael J. Wysocki /** 1717d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. 1718d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1719d23b9b00SRafael J. Wysocki */ 1720d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev) 1721d23b9b00SRafael J. Wysocki { 1722d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; 1723d23b9b00SRafael J. Wysocki 17240496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze_late(dev); 1725d23b9b00SRafael J. Wysocki } 1726d23b9b00SRafael J. Wysocki 1727d23b9b00SRafael J. Wysocki /** 1728d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. 1729d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1730d23b9b00SRafael J. Wysocki */ 1731d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev) 1732d23b9b00SRafael J. Wysocki { 1733d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; 1734d23b9b00SRafael J. Wysocki 17350496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw_early(dev); 1736d23b9b00SRafael J. Wysocki } 1737d23b9b00SRafael J. Wysocki 1738d23b9b00SRafael J. Wysocki /** 1739d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw - Default "device thaw" for PM domians. 1740d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1741d23b9b00SRafael J. Wysocki */ 1742d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev) 1743d23b9b00SRafael J. Wysocki { 1744d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; 1745d23b9b00SRafael J. Wysocki 1746d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw(dev); 1747d23b9b00SRafael J. Wysocki } 1748d23b9b00SRafael J. Wysocki 17490f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 17500f1d6986SRafael J. Wysocki 17510f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend NULL 17520f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late NULL 17530f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early NULL 17540f1d6986SRafael J. Wysocki #define pm_genpd_default_resume NULL 17550f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze NULL 17560f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late NULL 17570f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early NULL 17580f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw NULL 17590f1d6986SRafael J. Wysocki 17600f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */ 17610f1d6986SRafael J. Wysocki 1762d23b9b00SRafael J. Wysocki /** 1763f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1764f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1765f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1766f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 1767f721889fSRafael J. Wysocki */ 1768f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 1769f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1770f721889fSRafael J. Wysocki { 1771f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1772f721889fSRafael J. Wysocki return; 1773f721889fSRafael J. Wysocki 17745063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 17755063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1776f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1777f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 1778f721889fSRafael J. Wysocki genpd->gov = gov; 1779f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1780f721889fSRafael J. Wysocki genpd->in_progress = 0; 1781c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 178217b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 178317b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 1784c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 1785c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 1786596ba34bSRafael J. Wysocki genpd->device_count = 0; 1787221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 17886ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1789f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 1790f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 1791f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 1792596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 1793596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 17940496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 1795596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 1796596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 17970496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 1798596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 1799596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 18000496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 1801596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 1802596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 18030496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 1804596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 1805d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 18060496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 1807d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 1808596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 18090496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 1810d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 1811596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 1812ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 1813ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 1814c9914854SRafael J. Wysocki genpd->dev_ops.suspend = pm_genpd_default_suspend; 1815c9914854SRafael J. Wysocki genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late; 1816c9914854SRafael J. Wysocki genpd->dev_ops.resume_early = pm_genpd_default_resume_early; 1817c9914854SRafael J. Wysocki genpd->dev_ops.resume = pm_genpd_default_resume; 1818d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze = pm_genpd_default_freeze; 1819d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; 1820d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; 1821d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw = pm_genpd_default_thaw; 18225125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 18235125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 18245125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 18255125bbf3SRafael J. Wysocki } 1826