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 78e2e3e4e5SRafael J. Wysocki static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd, 79e2e3e4e5SRafael J. Wysocki struct device *dev) 80e2e3e4e5SRafael J. Wysocki { 81e2e3e4e5SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, start, dev); 82e2e3e4e5SRafael J. Wysocki } 83e2e3e4e5SRafael J. Wysocki 84c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 85f721889fSRafael J. Wysocki { 86c4bb3160SRafael J. Wysocki bool ret = false; 87c4bb3160SRafael J. Wysocki 88c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 89c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 90c4bb3160SRafael J. Wysocki 91c4bb3160SRafael J. Wysocki return ret; 92c4bb3160SRafael J. Wysocki } 93c4bb3160SRafael J. Wysocki 94c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 95c4bb3160SRafael J. Wysocki { 96c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 97c4bb3160SRafael J. Wysocki smp_mb__after_atomic_inc(); 98f721889fSRafael J. Wysocki } 99f721889fSRafael J. Wysocki 10017b75ecaSRafael J. Wysocki static void genpd_acquire_lock(struct generic_pm_domain *genpd) 10117b75ecaSRafael J. Wysocki { 10217b75ecaSRafael J. Wysocki DEFINE_WAIT(wait); 10317b75ecaSRafael J. Wysocki 10417b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 10517b75ecaSRafael J. Wysocki /* 10617b75ecaSRafael J. Wysocki * Wait for the domain to transition into either the active, 10717b75ecaSRafael J. Wysocki * or the power off state. 10817b75ecaSRafael J. Wysocki */ 10917b75ecaSRafael J. Wysocki for (;;) { 11017b75ecaSRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 11117b75ecaSRafael J. Wysocki TASK_UNINTERRUPTIBLE); 112c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 113c6d22b37SRafael J. Wysocki || genpd->status == GPD_STATE_POWER_OFF) 11417b75ecaSRafael J. Wysocki break; 11517b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 11617b75ecaSRafael J. Wysocki 11717b75ecaSRafael J. Wysocki schedule(); 11817b75ecaSRafael J. Wysocki 11917b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 12017b75ecaSRafael J. Wysocki } 12117b75ecaSRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 12217b75ecaSRafael J. Wysocki } 12317b75ecaSRafael J. Wysocki 12417b75ecaSRafael J. Wysocki static void genpd_release_lock(struct generic_pm_domain *genpd) 12517b75ecaSRafael J. Wysocki { 12617b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 12717b75ecaSRafael J. Wysocki } 12817b75ecaSRafael J. Wysocki 129c6d22b37SRafael J. Wysocki static void genpd_set_active(struct generic_pm_domain *genpd) 130c6d22b37SRafael J. Wysocki { 131c6d22b37SRafael J. Wysocki if (genpd->resume_count == 0) 132c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 133c6d22b37SRafael J. Wysocki } 134c6d22b37SRafael J. Wysocki 135cbc9ef02SRafael J. Wysocki static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) 136cbc9ef02SRafael J. Wysocki { 137cbc9ef02SRafael J. Wysocki s64 usecs64; 138cbc9ef02SRafael J. Wysocki 139cbc9ef02SRafael J. Wysocki if (!genpd->cpu_data) 140cbc9ef02SRafael J. Wysocki return; 141cbc9ef02SRafael J. Wysocki 142cbc9ef02SRafael J. Wysocki usecs64 = genpd->power_on_latency_ns; 143cbc9ef02SRafael J. Wysocki do_div(usecs64, NSEC_PER_USEC); 144cbc9ef02SRafael J. Wysocki usecs64 += genpd->cpu_data->saved_exit_latency; 145cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->exit_latency = usecs64; 146cbc9ef02SRafael J. Wysocki } 147cbc9ef02SRafael J. Wysocki 148f721889fSRafael J. Wysocki /** 1495063ce15SRafael J. Wysocki * __pm_genpd_poweron - Restore power to a given PM domain and its masters. 1505248051bSRafael J. Wysocki * @genpd: PM domain to power up. 1515248051bSRafael J. Wysocki * 1525063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 1535248051bSRafael J. Wysocki * resume a device belonging to it. 1545248051bSRafael J. Wysocki */ 1558951ef02SSachin Kamat static int __pm_genpd_poweron(struct generic_pm_domain *genpd) 1563f241775SRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 1575248051bSRafael J. Wysocki { 1585063ce15SRafael J. Wysocki struct gpd_link *link; 1593f241775SRafael J. Wysocki DEFINE_WAIT(wait); 1605248051bSRafael J. Wysocki int ret = 0; 1615248051bSRafael J. Wysocki 1625063ce15SRafael J. Wysocki /* If the domain's master is being waited for, we have to wait too. */ 1633f241775SRafael J. Wysocki for (;;) { 1643f241775SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 1653f241775SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 16617877eb5SRafael J. Wysocki if (genpd->status != GPD_STATE_WAIT_MASTER) 1673f241775SRafael J. Wysocki break; 1683f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 1693f241775SRafael J. Wysocki 1703f241775SRafael J. Wysocki schedule(); 1713f241775SRafael J. Wysocki 17217b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 1733f241775SRafael J. Wysocki } 1743f241775SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 17517b75ecaSRafael J. Wysocki 17617b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_ACTIVE 177596ba34bSRafael J. Wysocki || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 1783f241775SRafael J. Wysocki return 0; 1795248051bSRafael J. Wysocki 180c6d22b37SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) { 181c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 1823f241775SRafael J. Wysocki return 0; 183c6d22b37SRafael J. Wysocki } 184c6d22b37SRafael J. Wysocki 185cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 186cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 187cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = true; 188cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 189cbc9ef02SRafael J. Wysocki goto out; 190cbc9ef02SRafael J. Wysocki } 191cbc9ef02SRafael J. Wysocki 1925063ce15SRafael J. Wysocki /* 1935063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 1945063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 1955063ce15SRafael J. Wysocki * with it. 1965063ce15SRafael J. Wysocki */ 1975063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1985063ce15SRafael J. Wysocki genpd_sd_counter_inc(link->master); 19917877eb5SRafael J. Wysocki genpd->status = GPD_STATE_WAIT_MASTER; 2003c07cbc4SRafael J. Wysocki 2015248051bSRafael J. Wysocki mutex_unlock(&genpd->lock); 2025248051bSRafael J. Wysocki 2035063ce15SRafael J. Wysocki ret = pm_genpd_poweron(link->master); 2049e08cf42SRafael J. Wysocki 2059e08cf42SRafael J. Wysocki mutex_lock(&genpd->lock); 2069e08cf42SRafael J. Wysocki 2073f241775SRafael J. Wysocki /* 2083f241775SRafael J. Wysocki * The "wait for parent" status is guaranteed not to change 2095063ce15SRafael J. Wysocki * while the master is powering on. 2103f241775SRafael J. Wysocki */ 2113f241775SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 2123f241775SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 2135063ce15SRafael J. Wysocki if (ret) { 2145063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2159e08cf42SRafael J. Wysocki goto err; 2165248051bSRafael J. Wysocki } 2175063ce15SRafael J. Wysocki } 2185248051bSRafael J. Wysocki 2199e08cf42SRafael J. Wysocki if (genpd->power_on) { 2200140d8bdSRafael J. Wysocki ktime_t time_start = ktime_get(); 2210140d8bdSRafael J. Wysocki s64 elapsed_ns; 2220140d8bdSRafael J. Wysocki 223fe202fdeSRafael J. Wysocki ret = genpd->power_on(genpd); 2249e08cf42SRafael J. Wysocki if (ret) 2259e08cf42SRafael J. Wysocki goto err; 2260140d8bdSRafael J. Wysocki 2270140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 228e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_on_latency_ns) { 2290140d8bdSRafael J. Wysocki genpd->power_on_latency_ns = elapsed_ns; 2306ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 231cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 232e84b2c20SRafael J. Wysocki if (genpd->name) 233e84b2c20SRafael J. Wysocki pr_warning("%s: Power-on latency exceeded, " 234e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 235e84b2c20SRafael J. Wysocki elapsed_ns); 236e84b2c20SRafael J. Wysocki } 2373c07cbc4SRafael J. Wysocki } 2385248051bSRafael J. Wysocki 239cbc9ef02SRafael J. Wysocki out: 2409e08cf42SRafael J. Wysocki genpd_set_active(genpd); 2419e08cf42SRafael J. Wysocki 2423f241775SRafael J. Wysocki return 0; 2439e08cf42SRafael J. Wysocki 2449e08cf42SRafael J. Wysocki err: 2455063ce15SRafael J. Wysocki list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) 2465063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 2479e08cf42SRafael J. Wysocki 2483f241775SRafael J. Wysocki return ret; 2493f241775SRafael J. Wysocki } 2503f241775SRafael J. Wysocki 2513f241775SRafael J. Wysocki /** 2525063ce15SRafael J. Wysocki * pm_genpd_poweron - Restore power to a given PM domain and its masters. 2533f241775SRafael J. Wysocki * @genpd: PM domain to power up. 2543f241775SRafael J. Wysocki */ 2553f241775SRafael J. Wysocki int pm_genpd_poweron(struct generic_pm_domain *genpd) 2563f241775SRafael J. Wysocki { 2573f241775SRafael J. Wysocki int ret; 2583f241775SRafael J. Wysocki 2593f241775SRafael J. Wysocki mutex_lock(&genpd->lock); 2603f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 2613f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 2623f241775SRafael J. Wysocki return ret; 2635248051bSRafael J. Wysocki } 2645248051bSRafael J. Wysocki 2655248051bSRafael J. Wysocki #endif /* CONFIG_PM */ 2665248051bSRafael J. Wysocki 2675248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME 2685248051bSRafael J. Wysocki 2698e9afafdSRafael J. Wysocki static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) 2708e9afafdSRafael J. Wysocki { 2718e9afafdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, 2728e9afafdSRafael J. Wysocki save_state_latency_ns, "state save"); 2738e9afafdSRafael J. Wysocki } 2748e9afafdSRafael J. Wysocki 2758e9afafdSRafael J. Wysocki static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) 2768e9afafdSRafael J. Wysocki { 2778e9afafdSRafael J. Wysocki return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, 2788e9afafdSRafael J. Wysocki restore_state_latency_ns, 2798e9afafdSRafael J. Wysocki "state restore"); 2808e9afafdSRafael J. Wysocki } 2818e9afafdSRafael J. Wysocki 2826ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 2836ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 2846ff7bb0dSRafael J. Wysocki { 2856ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 2866ff7bb0dSRafael J. Wysocki struct device *dev; 2876ff7bb0dSRafael J. Wysocki 2886ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 2896ff7bb0dSRafael J. Wysocki 2906ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 2916ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 2926ff7bb0dSRafael J. Wysocki if (!dev) { 2936ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 2946ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 2956ff7bb0dSRafael J. Wysocki } 2966ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 2976ff7bb0dSRafael J. Wysocki 2986ff7bb0dSRafael J. Wysocki for (;;) { 2996ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 3006ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 3016ff7bb0dSRafael J. Wysocki 3026ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 3036ff7bb0dSRafael J. Wysocki 3046ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 3056ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 3061d5fcfecSRafael J. Wysocki if (pdd && pdd->dev) { 3076ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 3086ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 3096ff7bb0dSRafael J. Wysocki } else { 3106ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 3116ff7bb0dSRafael J. Wysocki } 3126ff7bb0dSRafael J. Wysocki 3136ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 3146ff7bb0dSRafael J. Wysocki 3156ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 3166ff7bb0dSRafael J. Wysocki mutex_lock(&genpd->lock); 3176ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 3186ff7bb0dSRafael J. Wysocki mutex_unlock(&genpd->lock); 3196ff7bb0dSRafael J. Wysocki } 3206ff7bb0dSRafael J. Wysocki 3216ff7bb0dSRafael J. Wysocki dev = dev->parent; 3226ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 3236ff7bb0dSRafael J. Wysocki break; 3246ff7bb0dSRafael J. Wysocki } 3256ff7bb0dSRafael J. Wysocki 3266ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 3276ff7bb0dSRafael J. Wysocki } 3286ff7bb0dSRafael J. Wysocki 3295248051bSRafael J. Wysocki /** 330f721889fSRafael J. Wysocki * __pm_genpd_save_device - Save the pre-suspend state of a device. 3314605ab65SRafael J. Wysocki * @pdd: Domain data of the device to save the state of. 332f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 333f721889fSRafael J. Wysocki */ 3344605ab65SRafael J. Wysocki static int __pm_genpd_save_device(struct pm_domain_data *pdd, 335f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 33617b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 337f721889fSRafael J. Wysocki { 338cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3394605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 340f721889fSRafael J. Wysocki int ret = 0; 341f721889fSRafael J. Wysocki 342cd0ea672SRafael J. Wysocki if (gpd_data->need_restore) 343f721889fSRafael J. Wysocki return 0; 344f721889fSRafael J. Wysocki 34517b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 34617b75ecaSRafael J. Wysocki 347d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 348ecf00475SRafael J. Wysocki ret = genpd_save_dev(genpd, dev); 349d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 350f721889fSRafael J. Wysocki 35117b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 35217b75ecaSRafael J. Wysocki 353f721889fSRafael J. Wysocki if (!ret) 354cd0ea672SRafael J. Wysocki gpd_data->need_restore = true; 355f721889fSRafael J. Wysocki 356f721889fSRafael J. Wysocki return ret; 357f721889fSRafael J. Wysocki } 358f721889fSRafael J. Wysocki 359f721889fSRafael J. Wysocki /** 360f721889fSRafael J. Wysocki * __pm_genpd_restore_device - Restore the pre-suspend state of a device. 3614605ab65SRafael J. Wysocki * @pdd: Domain data of the device to restore the state of. 362f721889fSRafael J. Wysocki * @genpd: PM domain the device belongs to. 363f721889fSRafael J. Wysocki */ 3644605ab65SRafael J. Wysocki static void __pm_genpd_restore_device(struct pm_domain_data *pdd, 365f721889fSRafael J. Wysocki struct generic_pm_domain *genpd) 36617b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 367f721889fSRafael J. Wysocki { 368cd0ea672SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); 3694605ab65SRafael J. Wysocki struct device *dev = pdd->dev; 37080de3d7fSRafael J. Wysocki bool need_restore = gpd_data->need_restore; 371f721889fSRafael J. Wysocki 37280de3d7fSRafael J. Wysocki gpd_data->need_restore = false; 37317b75ecaSRafael J. Wysocki mutex_unlock(&genpd->lock); 37417b75ecaSRafael J. Wysocki 375d5e4cbfeSRafael J. Wysocki genpd_start_dev(genpd, dev); 37680de3d7fSRafael J. Wysocki if (need_restore) 377ecf00475SRafael J. Wysocki genpd_restore_dev(genpd, dev); 378f721889fSRafael J. Wysocki 37917b75ecaSRafael J. Wysocki mutex_lock(&genpd->lock); 380f721889fSRafael J. Wysocki } 381f721889fSRafael J. Wysocki 382f721889fSRafael J. Wysocki /** 383c6d22b37SRafael J. Wysocki * genpd_abort_poweroff - Check if a PM domain power off should be aborted. 384c6d22b37SRafael J. Wysocki * @genpd: PM domain to check. 385c6d22b37SRafael J. Wysocki * 386c6d22b37SRafael J. Wysocki * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during 387c6d22b37SRafael J. Wysocki * a "power off" operation, which means that a "power on" has occured in the 388c6d22b37SRafael J. Wysocki * meantime, or if its resume_count field is different from zero, which means 389c6d22b37SRafael J. Wysocki * that one of its devices has been resumed in the meantime. 390c6d22b37SRafael J. Wysocki */ 391c6d22b37SRafael J. Wysocki static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 392c6d22b37SRafael J. Wysocki { 39317877eb5SRafael J. Wysocki return genpd->status == GPD_STATE_WAIT_MASTER 3943f241775SRafael J. Wysocki || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 395c6d22b37SRafael J. Wysocki } 396c6d22b37SRafael J. Wysocki 397c6d22b37SRafael J. Wysocki /** 39856375fd4SRafael J. Wysocki * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). 39956375fd4SRafael J. Wysocki * @genpd: PM domait to power off. 40056375fd4SRafael J. Wysocki * 40156375fd4SRafael J. Wysocki * Queue up the execution of pm_genpd_poweroff() unless it's already been done 40256375fd4SRafael J. Wysocki * before. 40356375fd4SRafael J. Wysocki */ 4040bc5b2deSRafael J. Wysocki void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 40556375fd4SRafael J. Wysocki { 40656375fd4SRafael J. Wysocki if (!work_pending(&genpd->power_off_work)) 40756375fd4SRafael J. Wysocki queue_work(pm_wq, &genpd->power_off_work); 40856375fd4SRafael J. Wysocki } 40956375fd4SRafael J. Wysocki 41056375fd4SRafael J. Wysocki /** 411f721889fSRafael J. Wysocki * pm_genpd_poweroff - Remove power from a given PM domain. 412f721889fSRafael J. Wysocki * @genpd: PM domain to power down. 413f721889fSRafael J. Wysocki * 414f721889fSRafael J. Wysocki * If all of the @genpd's devices have been suspended and all of its subdomains 415f721889fSRafael J. Wysocki * have been powered down, run the runtime suspend callbacks provided by all of 416f721889fSRafael J. Wysocki * the @genpd's devices' drivers and remove power from @genpd. 417f721889fSRafael J. Wysocki */ 418f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 41917b75ecaSRafael J. Wysocki __releases(&genpd->lock) __acquires(&genpd->lock) 420f721889fSRafael J. Wysocki { 4214605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 4225063ce15SRafael J. Wysocki struct gpd_link *link; 423f721889fSRafael J. Wysocki unsigned int not_suspended; 424c6d22b37SRafael J. Wysocki int ret = 0; 425f721889fSRafael J. Wysocki 426c6d22b37SRafael J. Wysocki start: 427c6d22b37SRafael J. Wysocki /* 428c6d22b37SRafael J. Wysocki * Do not try to power off the domain in the following situations: 429c6d22b37SRafael J. Wysocki * (1) The domain is already in the "power off" state. 4305063ce15SRafael J. Wysocki * (2) The domain is waiting for its master to power up. 431c6d22b37SRafael J. Wysocki * (3) One of the domain's devices is being resumed right now. 4323f241775SRafael J. Wysocki * (4) System suspend is in progress. 433c6d22b37SRafael J. Wysocki */ 4343f241775SRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 43517877eb5SRafael J. Wysocki || genpd->status == GPD_STATE_WAIT_MASTER 4363f241775SRafael J. Wysocki || genpd->resume_count > 0 || genpd->prepared_count > 0) 437f721889fSRafael J. Wysocki return 0; 438f721889fSRafael J. Wysocki 439c4bb3160SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) 440f721889fSRafael J. Wysocki return -EBUSY; 441f721889fSRafael J. Wysocki 442f721889fSRafael J. Wysocki not_suspended = 0; 4434605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 4440aa2a221SRafael J. Wysocki if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) 445feb70af0SRafael J. Wysocki || pdd->dev->power.irq_safe)) 446f721889fSRafael J. Wysocki not_suspended++; 447f721889fSRafael J. Wysocki 448f721889fSRafael J. Wysocki if (not_suspended > genpd->in_progress) 449f721889fSRafael J. Wysocki return -EBUSY; 450f721889fSRafael J. Wysocki 451c6d22b37SRafael J. Wysocki if (genpd->poweroff_task) { 452c6d22b37SRafael J. Wysocki /* 453c6d22b37SRafael J. Wysocki * Another instance of pm_genpd_poweroff() is executing 454c6d22b37SRafael J. Wysocki * callbacks, so tell it to start over and return. 455c6d22b37SRafael J. Wysocki */ 456c6d22b37SRafael J. Wysocki genpd->status = GPD_STATE_REPEAT; 457c6d22b37SRafael J. Wysocki return 0; 458c6d22b37SRafael J. Wysocki } 459c6d22b37SRafael J. Wysocki 460f721889fSRafael J. Wysocki if (genpd->gov && genpd->gov->power_down_ok) { 461f721889fSRafael J. Wysocki if (!genpd->gov->power_down_ok(&genpd->domain)) 462f721889fSRafael J. Wysocki return -EAGAIN; 463f721889fSRafael J. Wysocki } 464f721889fSRafael J. Wysocki 46517b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 466c6d22b37SRafael J. Wysocki genpd->poweroff_task = current; 46717b75ecaSRafael J. Wysocki 4684605ab65SRafael J. Wysocki list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { 4693c07cbc4SRafael J. Wysocki ret = atomic_read(&genpd->sd_count) == 0 ? 4704605ab65SRafael J. Wysocki __pm_genpd_save_device(pdd, genpd) : -EBUSY; 4713f241775SRafael J. Wysocki 4723f241775SRafael J. Wysocki if (genpd_abort_poweroff(genpd)) 4733f241775SRafael J. Wysocki goto out; 4743f241775SRafael J. Wysocki 475697a7f37SRafael J. Wysocki if (ret) { 476697a7f37SRafael J. Wysocki genpd_set_active(genpd); 477697a7f37SRafael J. Wysocki goto out; 478697a7f37SRafael J. Wysocki } 479f721889fSRafael J. Wysocki 480c6d22b37SRafael J. Wysocki if (genpd->status == GPD_STATE_REPEAT) { 481c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 482c6d22b37SRafael J. Wysocki goto start; 483c6d22b37SRafael J. Wysocki } 484c6d22b37SRafael J. Wysocki } 48517b75ecaSRafael J. Wysocki 486cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 487cbc9ef02SRafael J. Wysocki /* 488cbc9ef02SRafael J. Wysocki * If cpu_data is set, cpuidle should turn the domain off when 489cbc9ef02SRafael J. Wysocki * the CPU in it is idle. In that case we don't decrement the 490cbc9ef02SRafael J. Wysocki * subdomain counts of the master domains, so that power is not 491cbc9ef02SRafael J. Wysocki * removed from the current domain prematurely as a result of 492cbc9ef02SRafael J. Wysocki * cutting off the masters' power. 493cbc9ef02SRafael J. Wysocki */ 494cbc9ef02SRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 495cbc9ef02SRafael J. Wysocki cpuidle_pause_and_lock(); 496cbc9ef02SRafael J. Wysocki genpd->cpu_data->idle_state->disabled = false; 497cbc9ef02SRafael J. Wysocki cpuidle_resume_and_unlock(); 498cbc9ef02SRafael J. Wysocki goto out; 499cbc9ef02SRafael J. Wysocki } 500cbc9ef02SRafael J. Wysocki 5013c07cbc4SRafael J. Wysocki if (genpd->power_off) { 5020140d8bdSRafael J. Wysocki ktime_t time_start; 5030140d8bdSRafael J. Wysocki s64 elapsed_ns; 5040140d8bdSRafael J. Wysocki 5053c07cbc4SRafael J. Wysocki if (atomic_read(&genpd->sd_count) > 0) { 5063c07cbc4SRafael J. Wysocki ret = -EBUSY; 507c6d22b37SRafael J. Wysocki goto out; 508c6d22b37SRafael J. Wysocki } 50917b75ecaSRafael J. Wysocki 5100140d8bdSRafael J. Wysocki time_start = ktime_get(); 5110140d8bdSRafael J. Wysocki 5123c07cbc4SRafael J. Wysocki /* 5135063ce15SRafael J. Wysocki * If sd_count > 0 at this point, one of the subdomains hasn't 5145063ce15SRafael J. Wysocki * managed to call pm_genpd_poweron() for the master yet after 5153c07cbc4SRafael J. Wysocki * incrementing it. In that case pm_genpd_poweron() will wait 5163c07cbc4SRafael J. Wysocki * for us to drop the lock, so we can call .power_off() and let 5173c07cbc4SRafael J. Wysocki * the pm_genpd_poweron() restore power for us (this shouldn't 5183c07cbc4SRafael J. Wysocki * happen very often). 5193c07cbc4SRafael J. Wysocki */ 520d2805402SRafael J. Wysocki ret = genpd->power_off(genpd); 521d2805402SRafael J. Wysocki if (ret == -EBUSY) { 522d2805402SRafael J. Wysocki genpd_set_active(genpd); 523d2805402SRafael J. Wysocki goto out; 524d2805402SRafael J. Wysocki } 5250140d8bdSRafael J. Wysocki 5260140d8bdSRafael J. Wysocki elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 527e84b2c20SRafael J. Wysocki if (elapsed_ns > genpd->power_off_latency_ns) { 5280140d8bdSRafael J. Wysocki genpd->power_off_latency_ns = elapsed_ns; 5296ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 530e84b2c20SRafael J. Wysocki if (genpd->name) 531e84b2c20SRafael J. Wysocki pr_warning("%s: Power-off latency exceeded, " 532e84b2c20SRafael J. Wysocki "new value %lld ns\n", genpd->name, 533e84b2c20SRafael J. Wysocki elapsed_ns); 534e84b2c20SRafael J. Wysocki } 535d2805402SRafael J. Wysocki } 536f721889fSRafael J. Wysocki 53717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 538221e9b58SRafael J. Wysocki 5395063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 5405063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 5415063ce15SRafael J. Wysocki genpd_queue_power_off_work(link->master); 5425063ce15SRafael J. Wysocki } 54317b75ecaSRafael J. Wysocki 544c6d22b37SRafael J. Wysocki out: 545c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 546c6d22b37SRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 547c6d22b37SRafael J. Wysocki return ret; 548f721889fSRafael J. Wysocki } 549f721889fSRafael J. Wysocki 550f721889fSRafael J. Wysocki /** 551f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 552f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 553f721889fSRafael J. Wysocki */ 554f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 555f721889fSRafael J. Wysocki { 556f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 557f721889fSRafael J. Wysocki 558f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 559f721889fSRafael J. Wysocki 56017b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 561f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 56217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 563f721889fSRafael J. Wysocki } 564f721889fSRafael J. Wysocki 565f721889fSRafael J. Wysocki /** 566f721889fSRafael J. Wysocki * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 567f721889fSRafael J. Wysocki * @dev: Device to suspend. 568f721889fSRafael J. Wysocki * 569f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 570f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 571f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 572f721889fSRafael J. Wysocki */ 573f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev) 574f721889fSRafael J. Wysocki { 575f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 576b02c999aSRafael J. Wysocki bool (*stop_ok)(struct device *__dev); 577d5e4cbfeSRafael J. Wysocki int ret; 578f721889fSRafael J. Wysocki 579f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 580f721889fSRafael J. Wysocki 5815248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 5825248051bSRafael J. Wysocki if (IS_ERR(genpd)) 583f721889fSRafael J. Wysocki return -EINVAL; 584f721889fSRafael J. Wysocki 5850aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 5860aa2a221SRafael J. Wysocki 587b02c999aSRafael J. Wysocki stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; 588b02c999aSRafael J. Wysocki if (stop_ok && !stop_ok(dev)) 589b02c999aSRafael J. Wysocki return -EBUSY; 590b02c999aSRafael J. Wysocki 591d5e4cbfeSRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 592f721889fSRafael J. Wysocki if (ret) 59317b75ecaSRafael J. Wysocki return ret; 59417b75ecaSRafael J. Wysocki 5950aa2a221SRafael J. Wysocki /* 5960aa2a221SRafael J. Wysocki * If power.irq_safe is set, this routine will be run with interrupts 5970aa2a221SRafael J. Wysocki * off, so it can't use mutexes. 5980aa2a221SRafael J. Wysocki */ 5990aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 6000aa2a221SRafael J. Wysocki return 0; 6010aa2a221SRafael J. Wysocki 602c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 603f721889fSRafael J. Wysocki genpd->in_progress++; 604f721889fSRafael J. Wysocki pm_genpd_poweroff(genpd); 605f721889fSRafael J. Wysocki genpd->in_progress--; 606c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 607f721889fSRafael J. Wysocki 608f721889fSRafael J. Wysocki return 0; 609f721889fSRafael J. Wysocki } 610f721889fSRafael J. Wysocki 611f721889fSRafael J. Wysocki /** 612f721889fSRafael J. Wysocki * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. 613f721889fSRafael J. Wysocki * @dev: Device to resume. 614f721889fSRafael J. Wysocki * 615f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 616f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 617f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 618f721889fSRafael J. Wysocki */ 619f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev) 620f721889fSRafael J. Wysocki { 621f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 622c6d22b37SRafael J. Wysocki DEFINE_WAIT(wait); 623f721889fSRafael J. Wysocki int ret; 624f721889fSRafael J. Wysocki 625f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 626f721889fSRafael J. Wysocki 6275248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 6285248051bSRafael J. Wysocki if (IS_ERR(genpd)) 629f721889fSRafael J. Wysocki return -EINVAL; 630f721889fSRafael J. Wysocki 6310aa2a221SRafael J. Wysocki might_sleep_if(!genpd->dev_irq_safe); 6320aa2a221SRafael J. Wysocki 6330aa2a221SRafael J. Wysocki /* If power.irq_safe, the PM domain is never powered off. */ 6340aa2a221SRafael J. Wysocki if (dev->power.irq_safe) 635e2e3e4e5SRafael J. Wysocki return genpd_start_dev_no_timing(genpd, dev); 6360aa2a221SRafael J. Wysocki 637c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 6383f241775SRafael J. Wysocki ret = __pm_genpd_poweron(genpd); 6393f241775SRafael J. Wysocki if (ret) { 6403f241775SRafael J. Wysocki mutex_unlock(&genpd->lock); 6413f241775SRafael J. Wysocki return ret; 6423f241775SRafael J. Wysocki } 64317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_BUSY; 644c6d22b37SRafael J. Wysocki genpd->resume_count++; 645c6d22b37SRafael J. Wysocki for (;;) { 646c6d22b37SRafael J. Wysocki prepare_to_wait(&genpd->status_wait_queue, &wait, 647c6d22b37SRafael J. Wysocki TASK_UNINTERRUPTIBLE); 648c6d22b37SRafael J. Wysocki /* 649c6d22b37SRafael J. Wysocki * If current is the powering off task, we have been called 650c6d22b37SRafael J. Wysocki * reentrantly from one of the device callbacks, so we should 651c6d22b37SRafael J. Wysocki * not wait. 652c6d22b37SRafael J. Wysocki */ 653c6d22b37SRafael J. Wysocki if (!genpd->poweroff_task || genpd->poweroff_task == current) 654c6d22b37SRafael J. Wysocki break; 655c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 656c6d22b37SRafael J. Wysocki 657c6d22b37SRafael J. Wysocki schedule(); 658c6d22b37SRafael J. Wysocki 659c6d22b37SRafael J. Wysocki mutex_lock(&genpd->lock); 660c6d22b37SRafael J. Wysocki } 661c6d22b37SRafael J. Wysocki finish_wait(&genpd->status_wait_queue, &wait); 662cd0ea672SRafael J. Wysocki __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); 663c6d22b37SRafael J. Wysocki genpd->resume_count--; 664c6d22b37SRafael J. Wysocki genpd_set_active(genpd); 66517b75ecaSRafael J. Wysocki wake_up_all(&genpd->status_wait_queue); 666c6d22b37SRafael J. Wysocki mutex_unlock(&genpd->lock); 66717b75ecaSRafael J. Wysocki 668f721889fSRafael J. Wysocki return 0; 669f721889fSRafael J. Wysocki } 670f721889fSRafael J. Wysocki 67117f2ae7fSRafael J. Wysocki /** 67217f2ae7fSRafael J. Wysocki * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. 67317f2ae7fSRafael J. Wysocki */ 67417f2ae7fSRafael J. Wysocki void pm_genpd_poweroff_unused(void) 67517f2ae7fSRafael J. Wysocki { 67617f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 67717f2ae7fSRafael J. Wysocki 67817f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 67917f2ae7fSRafael J. Wysocki 68017f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 68117f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 68217f2ae7fSRafael J. Wysocki 68317f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 68417f2ae7fSRafael J. Wysocki } 68517f2ae7fSRafael J. Wysocki 686f721889fSRafael J. Wysocki #else 687f721889fSRafael J. Wysocki 6886ff7bb0dSRafael J. Wysocki static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 6896ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 6906ff7bb0dSRafael J. Wysocki { 6916ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 6926ff7bb0dSRafael J. Wysocki } 6936ff7bb0dSRafael J. Wysocki 694f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {} 695f721889fSRafael J. Wysocki 696f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend NULL 697f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume NULL 698f721889fSRafael J. Wysocki 699f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */ 700f721889fSRafael J. Wysocki 701596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 702596ba34bSRafael J. Wysocki 70377f827deSRafael J. Wysocki /** 70477f827deSRafael J. Wysocki * pm_genpd_present - Check if the given PM domain has been initialized. 70577f827deSRafael J. Wysocki * @genpd: PM domain to check. 70677f827deSRafael J. Wysocki */ 70777f827deSRafael J. Wysocki static bool pm_genpd_present(struct generic_pm_domain *genpd) 70877f827deSRafael J. Wysocki { 70977f827deSRafael J. Wysocki struct generic_pm_domain *gpd; 71077f827deSRafael J. Wysocki 71177f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 71277f827deSRafael J. Wysocki return false; 71377f827deSRafael J. Wysocki 71477f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 71577f827deSRafael J. Wysocki if (gpd == genpd) 71677f827deSRafael J. Wysocki return true; 71777f827deSRafael J. Wysocki 71877f827deSRafael J. Wysocki return false; 71977f827deSRafael J. Wysocki } 72077f827deSRafael J. Wysocki 721d5e4cbfeSRafael J. Wysocki static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, 722d5e4cbfeSRafael J. Wysocki struct device *dev) 723d5e4cbfeSRafael J. Wysocki { 724d5e4cbfeSRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); 725d5e4cbfeSRafael J. Wysocki } 726d5e4cbfeSRafael J. Wysocki 727d23b9b00SRafael J. Wysocki static int genpd_suspend_dev(struct generic_pm_domain *genpd, struct device *dev) 728d23b9b00SRafael J. Wysocki { 729d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend, dev); 730d23b9b00SRafael J. Wysocki } 731d23b9b00SRafael J. Wysocki 732d23b9b00SRafael J. Wysocki static int genpd_suspend_late(struct generic_pm_domain *genpd, struct device *dev) 733d23b9b00SRafael J. Wysocki { 734d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, suspend_late, dev); 735d23b9b00SRafael J. Wysocki } 736d23b9b00SRafael J. Wysocki 737d23b9b00SRafael J. Wysocki static int genpd_resume_early(struct generic_pm_domain *genpd, struct device *dev) 738d23b9b00SRafael J. Wysocki { 739d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume_early, dev); 740d23b9b00SRafael J. Wysocki } 741d23b9b00SRafael J. Wysocki 742d23b9b00SRafael J. Wysocki static int genpd_resume_dev(struct generic_pm_domain *genpd, struct device *dev) 743d23b9b00SRafael J. Wysocki { 744d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, resume, dev); 745d23b9b00SRafael J. Wysocki } 746d23b9b00SRafael J. Wysocki 747d23b9b00SRafael J. Wysocki static int genpd_freeze_dev(struct generic_pm_domain *genpd, struct device *dev) 748d23b9b00SRafael J. Wysocki { 749d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze, dev); 750d23b9b00SRafael J. Wysocki } 751d23b9b00SRafael J. Wysocki 752d23b9b00SRafael J. Wysocki static int genpd_freeze_late(struct generic_pm_domain *genpd, struct device *dev) 753d23b9b00SRafael J. Wysocki { 754d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, freeze_late, dev); 755d23b9b00SRafael J. Wysocki } 756d23b9b00SRafael J. Wysocki 757d23b9b00SRafael J. Wysocki static int genpd_thaw_early(struct generic_pm_domain *genpd, struct device *dev) 758d23b9b00SRafael J. Wysocki { 759d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw_early, dev); 760d23b9b00SRafael J. Wysocki } 761d23b9b00SRafael J. Wysocki 762d23b9b00SRafael J. Wysocki static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) 763d23b9b00SRafael J. Wysocki { 764d23b9b00SRafael J. Wysocki return GENPD_DEV_CALLBACK(genpd, int, thaw, dev); 765d23b9b00SRafael J. Wysocki } 766d23b9b00SRafael J. Wysocki 767596ba34bSRafael J. Wysocki /** 7685063ce15SRafael J. Wysocki * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. 769596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 770596ba34bSRafael J. Wysocki * 771596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 7725063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 773596ba34bSRafael J. Wysocki * 77477f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 77577f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 77677f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 77777f827deSRafael J. Wysocki * parallel). 778596ba34bSRafael J. Wysocki */ 779596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 780596ba34bSRafael J. Wysocki { 7815063ce15SRafael J. Wysocki struct gpd_link *link; 782596ba34bSRafael J. Wysocki 78317b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF) 784596ba34bSRafael J. Wysocki return; 785596ba34bSRafael J. Wysocki 786c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 787c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 788596ba34bSRafael J. Wysocki return; 789596ba34bSRafael J. Wysocki 790596ba34bSRafael J. Wysocki if (genpd->power_off) 791596ba34bSRafael J. Wysocki genpd->power_off(genpd); 792596ba34bSRafael J. Wysocki 79317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 7945063ce15SRafael J. Wysocki 7955063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 7965063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 7975063ce15SRafael J. Wysocki pm_genpd_sync_poweroff(link->master); 798596ba34bSRafael J. Wysocki } 799596ba34bSRafael J. Wysocki } 800596ba34bSRafael J. Wysocki 801596ba34bSRafael J. Wysocki /** 802802d8b49SRafael J. Wysocki * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. 803802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 804802d8b49SRafael J. Wysocki * 80577f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 80677f827deSRafael J. Wysocki * transitions, so it need not acquire locks (all of the "noirq" callbacks are 80777f827deSRafael J. Wysocki * executed sequentially, so it is guaranteed that it will never run twice in 80877f827deSRafael J. Wysocki * parallel). 809802d8b49SRafael J. Wysocki */ 810802d8b49SRafael J. Wysocki static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) 811802d8b49SRafael J. Wysocki { 812802d8b49SRafael J. Wysocki struct gpd_link *link; 813802d8b49SRafael J. Wysocki 814802d8b49SRafael J. Wysocki if (genpd->status != GPD_STATE_POWER_OFF) 815802d8b49SRafael J. Wysocki return; 816802d8b49SRafael J. Wysocki 817802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 818802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(link->master); 819802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 820802d8b49SRafael J. Wysocki } 821802d8b49SRafael J. Wysocki 822802d8b49SRafael J. Wysocki if (genpd->power_on) 823802d8b49SRafael J. Wysocki genpd->power_on(genpd); 824802d8b49SRafael J. Wysocki 825802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 826802d8b49SRafael J. Wysocki } 827802d8b49SRafael J. Wysocki 828802d8b49SRafael J. Wysocki /** 8294ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 8304ecd6e65SRafael J. Wysocki * @dev: Device to check. 8314ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 8324ecd6e65SRafael J. Wysocki * 8334ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 8344ecd6e65SRafael J. Wysocki * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled 8354ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 8364ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 8374ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 8384ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 8394ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 8404ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 8414ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 8424ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 8434ecd6e65SRafael J. Wysocki */ 8444ecd6e65SRafael J. Wysocki static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) 8454ecd6e65SRafael J. Wysocki { 8464ecd6e65SRafael J. Wysocki bool active_wakeup; 8474ecd6e65SRafael J. Wysocki 8484ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 8494ecd6e65SRafael J. Wysocki return false; 8504ecd6e65SRafael J. Wysocki 851d5e4cbfeSRafael J. Wysocki active_wakeup = genpd_dev_active_wakeup(genpd, dev); 8524ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 8534ecd6e65SRafael J. Wysocki } 8544ecd6e65SRafael J. Wysocki 8554ecd6e65SRafael J. Wysocki /** 856596ba34bSRafael J. Wysocki * pm_genpd_prepare - Start power transition of a device in a PM domain. 857596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 858596ba34bSRafael J. Wysocki * 859596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 860596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 861596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 862596ba34bSRafael J. Wysocki * consisting of I/O devices. 863596ba34bSRafael J. Wysocki */ 864596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev) 865596ba34bSRafael J. Wysocki { 866596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 867b6c10c84SRafael J. Wysocki int ret; 868596ba34bSRafael J. Wysocki 869596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 870596ba34bSRafael J. Wysocki 871596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 872596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 873596ba34bSRafael J. Wysocki return -EINVAL; 874596ba34bSRafael J. Wysocki 87517b75ecaSRafael J. Wysocki /* 87617b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 87717b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 87817b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 87917b75ecaSRafael J. Wysocki */ 88017b75ecaSRafael J. Wysocki pm_runtime_get_noresume(dev); 88117b75ecaSRafael J. Wysocki if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) 88217b75ecaSRafael J. Wysocki pm_wakeup_event(dev, 0); 88317b75ecaSRafael J. Wysocki 88417b75ecaSRafael J. Wysocki if (pm_wakeup_pending()) { 88517b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 88617b75ecaSRafael J. Wysocki return -EBUSY; 88717b75ecaSRafael J. Wysocki } 88817b75ecaSRafael J. Wysocki 8894ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 8904ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 8914ecd6e65SRafael J. Wysocki 89217b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 893596ba34bSRafael J. Wysocki 89465533bbfSRafael J. Wysocki if (genpd->prepared_count++ == 0) { 89565533bbfSRafael J. Wysocki genpd->suspended_count = 0; 89617b75ecaSRafael J. Wysocki genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; 89765533bbfSRafael J. Wysocki } 89817b75ecaSRafael J. Wysocki 89917b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 900596ba34bSRafael J. Wysocki 901596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 90217b75ecaSRafael J. Wysocki pm_runtime_put_noidle(dev); 903596ba34bSRafael J. Wysocki return 0; 904596ba34bSRafael J. Wysocki } 905596ba34bSRafael J. Wysocki 906596ba34bSRafael J. Wysocki /* 90717b75ecaSRafael J. Wysocki * The PM domain must be in the GPD_STATE_ACTIVE state at this point, 90817b75ecaSRafael J. Wysocki * so pm_genpd_poweron() will return immediately, but if the device 909d5e4cbfeSRafael J. Wysocki * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need 91017b75ecaSRafael J. Wysocki * to make it operational. 911596ba34bSRafael J. Wysocki */ 91217b75ecaSRafael J. Wysocki pm_runtime_resume(dev); 913596ba34bSRafael J. Wysocki __pm_runtime_disable(dev, false); 914596ba34bSRafael J. Wysocki 915b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 916b6c10c84SRafael J. Wysocki if (ret) { 917b6c10c84SRafael J. Wysocki mutex_lock(&genpd->lock); 918b6c10c84SRafael J. Wysocki 919b6c10c84SRafael J. Wysocki if (--genpd->prepared_count == 0) 920b6c10c84SRafael J. Wysocki genpd->suspend_power_off = false; 921b6c10c84SRafael J. Wysocki 922b6c10c84SRafael J. Wysocki mutex_unlock(&genpd->lock); 92317b75ecaSRafael J. Wysocki pm_runtime_enable(dev); 924b6c10c84SRafael J. Wysocki } 92517b75ecaSRafael J. Wysocki 92617b75ecaSRafael J. Wysocki pm_runtime_put_sync(dev); 927b6c10c84SRafael J. Wysocki return ret; 928596ba34bSRafael J. Wysocki } 929596ba34bSRafael J. Wysocki 930596ba34bSRafael J. Wysocki /** 931596ba34bSRafael J. Wysocki * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain. 932596ba34bSRafael J. Wysocki * @dev: Device to suspend. 933596ba34bSRafael J. Wysocki * 934596ba34bSRafael J. Wysocki * Suspend a device under the assumption that its pm_domain field points to the 935596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 936596ba34bSRafael J. Wysocki * a PM domain consisting of I/O devices. 937596ba34bSRafael J. Wysocki */ 938596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev) 939596ba34bSRafael J. Wysocki { 940596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 941596ba34bSRafael J. Wysocki 942596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 943596ba34bSRafael J. Wysocki 944596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 945596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 946596ba34bSRafael J. Wysocki return -EINVAL; 947596ba34bSRafael J. Wysocki 948d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_dev(genpd, dev); 949596ba34bSRafael J. Wysocki } 950596ba34bSRafael J. Wysocki 951596ba34bSRafael J. Wysocki /** 9520496c8aeSRafael J. Wysocki * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. 953596ba34bSRafael J. Wysocki * @dev: Device to suspend. 954596ba34bSRafael J. Wysocki * 955596ba34bSRafael J. Wysocki * Carry out a late suspend of a device under the assumption that its 956596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 957596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 958596ba34bSRafael J. Wysocki */ 9590496c8aeSRafael J. Wysocki static int pm_genpd_suspend_late(struct device *dev) 960596ba34bSRafael J. Wysocki { 961596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 962596ba34bSRafael J. Wysocki 963596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 964596ba34bSRafael J. Wysocki 965596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 966596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 967596ba34bSRafael J. Wysocki return -EINVAL; 968596ba34bSRafael J. Wysocki 9690496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); 9700496c8aeSRafael J. Wysocki } 971596ba34bSRafael J. Wysocki 9720496c8aeSRafael J. Wysocki /** 9730496c8aeSRafael J. Wysocki * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 9740496c8aeSRafael J. Wysocki * @dev: Device to suspend. 9750496c8aeSRafael J. Wysocki * 9760496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 9770496c8aeSRafael J. Wysocki * been stopped. 9780496c8aeSRafael J. Wysocki */ 9790496c8aeSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev) 9800496c8aeSRafael J. Wysocki { 9810496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 982596ba34bSRafael J. Wysocki 9830496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 9840496c8aeSRafael J. Wysocki 9850496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 9860496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 9870496c8aeSRafael J. Wysocki return -EINVAL; 9880496c8aeSRafael J. Wysocki 989dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 9900496c8aeSRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 991d4f2d87aSRafael J. Wysocki return 0; 992d4f2d87aSRafael J. Wysocki 993d5e4cbfeSRafael J. Wysocki genpd_stop_dev(genpd, dev); 994596ba34bSRafael J. Wysocki 995596ba34bSRafael J. Wysocki /* 996596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 997596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 998596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 999596ba34bSRafael J. Wysocki */ 1000596ba34bSRafael J. Wysocki genpd->suspended_count++; 1001596ba34bSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 1002596ba34bSRafael J. Wysocki 1003596ba34bSRafael J. Wysocki return 0; 1004596ba34bSRafael J. Wysocki } 1005596ba34bSRafael J. Wysocki 1006596ba34bSRafael J. Wysocki /** 10070496c8aeSRafael J. Wysocki * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. 1008596ba34bSRafael J. Wysocki * @dev: Device to resume. 1009596ba34bSRafael J. Wysocki * 10100496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 1011596ba34bSRafael J. Wysocki */ 1012596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev) 1013596ba34bSRafael J. Wysocki { 1014596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1015596ba34bSRafael J. Wysocki 1016596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1017596ba34bSRafael J. Wysocki 1018596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1019596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1020596ba34bSRafael J. Wysocki return -EINVAL; 1021596ba34bSRafael J. Wysocki 1022dbf37414SRafael J. Wysocki if (genpd->suspend_power_off 1023cc85b207SRafael J. Wysocki || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) 1024596ba34bSRafael J. Wysocki return 0; 1025596ba34bSRafael J. Wysocki 1026596ba34bSRafael J. Wysocki /* 1027596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1028596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1029596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 1030596ba34bSRafael J. Wysocki */ 1031802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(genpd); 1032596ba34bSRafael J. Wysocki genpd->suspended_count--; 1033596ba34bSRafael J. Wysocki 10340496c8aeSRafael J. Wysocki return genpd_start_dev(genpd, dev); 1035596ba34bSRafael J. Wysocki } 1036596ba34bSRafael J. Wysocki 1037596ba34bSRafael J. Wysocki /** 10380496c8aeSRafael J. Wysocki * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. 10390496c8aeSRafael J. Wysocki * @dev: Device to resume. 10400496c8aeSRafael J. Wysocki * 10410496c8aeSRafael J. Wysocki * Carry out an early resume of a device under the assumption that its 10420496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 10430496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 10440496c8aeSRafael J. Wysocki * devices. 10450496c8aeSRafael J. Wysocki */ 10460496c8aeSRafael J. Wysocki static int pm_genpd_resume_early(struct device *dev) 10470496c8aeSRafael J. Wysocki { 10480496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 10490496c8aeSRafael J. Wysocki 10500496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 10510496c8aeSRafael J. Wysocki 10520496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 10530496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 10540496c8aeSRafael J. Wysocki return -EINVAL; 10550496c8aeSRafael J. Wysocki 10560496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); 10570496c8aeSRafael J. Wysocki } 10580496c8aeSRafael J. Wysocki 10590496c8aeSRafael J. Wysocki /** 10600496c8aeSRafael J. Wysocki * pm_genpd_resume - Resume of device in an I/O PM domain. 1061596ba34bSRafael J. Wysocki * @dev: Device to resume. 1062596ba34bSRafael J. Wysocki * 1063596ba34bSRafael J. Wysocki * Resume a device under the assumption that its pm_domain field points to the 1064596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1065596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1066596ba34bSRafael J. Wysocki */ 1067596ba34bSRafael J. Wysocki static int pm_genpd_resume(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 1077d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_resume_dev(genpd, dev); 1078596ba34bSRafael J. Wysocki } 1079596ba34bSRafael J. Wysocki 1080596ba34bSRafael J. Wysocki /** 10810496c8aeSRafael J. Wysocki * pm_genpd_freeze - Freezing a device in an I/O PM domain. 1082596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1083596ba34bSRafael J. Wysocki * 1084596ba34bSRafael J. Wysocki * Freeze a device under the assumption that its pm_domain field points to the 1085596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1086596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1087596ba34bSRafael J. Wysocki */ 1088596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev) 1089596ba34bSRafael J. Wysocki { 1090596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1091596ba34bSRafael J. Wysocki 1092596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1093596ba34bSRafael J. Wysocki 1094596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1095596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1096596ba34bSRafael J. Wysocki return -EINVAL; 1097596ba34bSRafael J. Wysocki 1098d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_dev(genpd, dev); 1099596ba34bSRafael J. Wysocki } 1100596ba34bSRafael J. Wysocki 1101596ba34bSRafael J. Wysocki /** 11020496c8aeSRafael J. Wysocki * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. 11030496c8aeSRafael J. Wysocki * @dev: Device to freeze. 11040496c8aeSRafael J. Wysocki * 11050496c8aeSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 11060496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 11070496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 11080496c8aeSRafael J. Wysocki * devices. 11090496c8aeSRafael J. Wysocki */ 11100496c8aeSRafael J. Wysocki static int pm_genpd_freeze_late(struct device *dev) 11110496c8aeSRafael J. Wysocki { 11120496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 11130496c8aeSRafael J. Wysocki 11140496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 11150496c8aeSRafael J. Wysocki 11160496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11170496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11180496c8aeSRafael J. Wysocki return -EINVAL; 11190496c8aeSRafael J. Wysocki 11200496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); 11210496c8aeSRafael J. Wysocki } 11220496c8aeSRafael J. Wysocki 11230496c8aeSRafael J. Wysocki /** 11240496c8aeSRafael J. Wysocki * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1125596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1126596ba34bSRafael J. Wysocki * 1127596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1128596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1129596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1130596ba34bSRafael J. Wysocki * devices. 1131596ba34bSRafael J. Wysocki */ 1132596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev) 1133596ba34bSRafael J. Wysocki { 1134596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1135596ba34bSRafael J. Wysocki 1136596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1137596ba34bSRafael J. Wysocki 1138596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1139596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1140596ba34bSRafael J. Wysocki return -EINVAL; 1141596ba34bSRafael J. Wysocki 1142dbf37414SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); 1143596ba34bSRafael J. Wysocki } 1144596ba34bSRafael J. Wysocki 1145596ba34bSRafael J. Wysocki /** 11460496c8aeSRafael J. Wysocki * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1147596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1148596ba34bSRafael J. Wysocki * 11490496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 11500496c8aeSRafael J. Wysocki * before the system transition. 1151596ba34bSRafael J. Wysocki */ 1152596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev) 1153596ba34bSRafael J. Wysocki { 1154596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1155596ba34bSRafael J. Wysocki 1156596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1157596ba34bSRafael J. Wysocki 1158596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1159596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1160596ba34bSRafael J. Wysocki return -EINVAL; 1161596ba34bSRafael J. Wysocki 1162dbf37414SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); 11630496c8aeSRafael J. Wysocki } 1164596ba34bSRafael J. Wysocki 11650496c8aeSRafael J. Wysocki /** 11660496c8aeSRafael J. Wysocki * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. 11670496c8aeSRafael J. Wysocki * @dev: Device to thaw. 11680496c8aeSRafael J. Wysocki * 11690496c8aeSRafael J. Wysocki * Carry out an early thaw of a device under the assumption that its 11700496c8aeSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 11710496c8aeSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 11720496c8aeSRafael J. Wysocki * devices. 11730496c8aeSRafael J. Wysocki */ 11740496c8aeSRafael J. Wysocki static int pm_genpd_thaw_early(struct device *dev) 11750496c8aeSRafael J. Wysocki { 11760496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1177596ba34bSRafael J. Wysocki 11780496c8aeSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 11790496c8aeSRafael J. Wysocki 11800496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11810496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11820496c8aeSRafael J. Wysocki return -EINVAL; 11830496c8aeSRafael J. Wysocki 11840496c8aeSRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); 1185596ba34bSRafael J. Wysocki } 1186596ba34bSRafael J. Wysocki 1187596ba34bSRafael J. Wysocki /** 1188596ba34bSRafael J. Wysocki * pm_genpd_thaw - Thaw a device belonging to an I/O power domain. 1189596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1190596ba34bSRafael J. Wysocki * 1191596ba34bSRafael J. Wysocki * Thaw a device under the assumption that its pm_domain field points to the 1192596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1193596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1194596ba34bSRafael J. Wysocki */ 1195596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev) 1196596ba34bSRafael J. Wysocki { 1197596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1198596ba34bSRafael J. Wysocki 1199596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1200596ba34bSRafael J. Wysocki 1201596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1202596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1203596ba34bSRafael J. Wysocki return -EINVAL; 1204596ba34bSRafael J. Wysocki 1205d23b9b00SRafael J. Wysocki return genpd->suspend_power_off ? 0 : genpd_thaw_dev(genpd, dev); 1206596ba34bSRafael J. Wysocki } 1207596ba34bSRafael J. Wysocki 1208596ba34bSRafael J. Wysocki /** 12090496c8aeSRafael J. Wysocki * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1210596ba34bSRafael J. Wysocki * @dev: Device to resume. 1211596ba34bSRafael J. Wysocki * 12120496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 12130496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1214596ba34bSRafael J. Wysocki */ 1215596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev) 1216596ba34bSRafael J. Wysocki { 1217596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1218596ba34bSRafael J. Wysocki 1219596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1220596ba34bSRafael J. Wysocki 1221596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1222596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1223596ba34bSRafael J. Wysocki return -EINVAL; 1224596ba34bSRafael J. Wysocki 1225596ba34bSRafael J. Wysocki /* 1226596ba34bSRafael J. Wysocki * Since all of the "noirq" callbacks are executed sequentially, it is 1227596ba34bSRafael J. Wysocki * guaranteed that this function will never run twice in parallel for 1228596ba34bSRafael J. Wysocki * the same PM domain, so it is not necessary to use locking here. 122965533bbfSRafael J. Wysocki * 123065533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 123165533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 123265533bbfSRafael J. Wysocki */ 123365533bbfSRafael J. Wysocki if (genpd->suspended_count++ == 0) { 123465533bbfSRafael J. Wysocki /* 123565533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 1236802d8b49SRafael J. Wysocki * so make it appear as powered off to pm_genpd_sync_poweron(), 1237802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1238596ba34bSRafael J. Wysocki */ 123917b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 1240596ba34bSRafael J. Wysocki if (genpd->suspend_power_off) { 1241596ba34bSRafael J. Wysocki /* 124265533bbfSRafael J. Wysocki * If the domain was off before the hibernation, make 124365533bbfSRafael J. Wysocki * sure it will be off going forward. 1244596ba34bSRafael J. Wysocki */ 1245596ba34bSRafael J. Wysocki if (genpd->power_off) 1246596ba34bSRafael J. Wysocki genpd->power_off(genpd); 124765533bbfSRafael J. Wysocki 1248596ba34bSRafael J. Wysocki return 0; 1249596ba34bSRafael J. Wysocki } 125065533bbfSRafael J. Wysocki } 1251596ba34bSRafael J. Wysocki 125218dd2eceSRafael J. Wysocki if (genpd->suspend_power_off) 125318dd2eceSRafael J. Wysocki return 0; 125418dd2eceSRafael J. Wysocki 1255802d8b49SRafael J. Wysocki pm_genpd_sync_poweron(genpd); 1256596ba34bSRafael J. Wysocki 1257dbf37414SRafael J. Wysocki return genpd_start_dev(genpd, dev); 1258596ba34bSRafael J. Wysocki } 1259596ba34bSRafael J. Wysocki 1260596ba34bSRafael J. Wysocki /** 1261596ba34bSRafael J. Wysocki * pm_genpd_complete - Complete power transition of a device in a power domain. 1262596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1263596ba34bSRafael J. Wysocki * 1264596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1265596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1266596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1267596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1268596ba34bSRafael J. Wysocki */ 1269596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev) 1270596ba34bSRafael J. Wysocki { 1271596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1272596ba34bSRafael J. Wysocki bool run_complete; 1273596ba34bSRafael J. Wysocki 1274596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1275596ba34bSRafael J. Wysocki 1276596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1277596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1278596ba34bSRafael J. Wysocki return; 1279596ba34bSRafael J. Wysocki 1280596ba34bSRafael J. Wysocki mutex_lock(&genpd->lock); 1281596ba34bSRafael J. Wysocki 1282596ba34bSRafael J. Wysocki run_complete = !genpd->suspend_power_off; 1283596ba34bSRafael J. Wysocki if (--genpd->prepared_count == 0) 1284596ba34bSRafael J. Wysocki genpd->suspend_power_off = false; 1285596ba34bSRafael J. Wysocki 1286596ba34bSRafael J. Wysocki mutex_unlock(&genpd->lock); 1287596ba34bSRafael J. Wysocki 1288596ba34bSRafael J. Wysocki if (run_complete) { 1289596ba34bSRafael J. Wysocki pm_generic_complete(dev); 12906f00ff78SRafael J. Wysocki pm_runtime_set_active(dev); 1291596ba34bSRafael J. Wysocki pm_runtime_enable(dev); 12926f00ff78SRafael J. Wysocki pm_runtime_idle(dev); 1293596ba34bSRafael J. Wysocki } 1294596ba34bSRafael J. Wysocki } 1295596ba34bSRafael J. Wysocki 129677f827deSRafael J. Wysocki /** 129777f827deSRafael J. Wysocki * pm_genpd_syscore_switch - Switch power during system core suspend or resume. 129877f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 129977f827deSRafael J. Wysocki * 130077f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 130177f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 130277f827deSRafael J. Wysocki */ 130377f827deSRafael J. Wysocki void pm_genpd_syscore_switch(struct device *dev, bool suspend) 130477f827deSRafael J. Wysocki { 130577f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 130677f827deSRafael J. Wysocki 130777f827deSRafael J. Wysocki genpd = dev_to_genpd(dev); 130877f827deSRafael J. Wysocki if (!pm_genpd_present(genpd)) 130977f827deSRafael J. Wysocki return; 131077f827deSRafael J. Wysocki 131177f827deSRafael J. Wysocki if (suspend) { 131277f827deSRafael J. Wysocki genpd->suspended_count++; 131377f827deSRafael J. Wysocki pm_genpd_sync_poweroff(genpd); 131477f827deSRafael J. Wysocki } else { 131577f827deSRafael J. Wysocki pm_genpd_sync_poweron(genpd); 131677f827deSRafael J. Wysocki genpd->suspended_count--; 131777f827deSRafael J. Wysocki } 131877f827deSRafael J. Wysocki } 131977f827deSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch); 132077f827deSRafael J. Wysocki 1321596ba34bSRafael J. Wysocki #else 1322596ba34bSRafael J. Wysocki 1323596ba34bSRafael J. Wysocki #define pm_genpd_prepare NULL 1324596ba34bSRafael J. Wysocki #define pm_genpd_suspend NULL 13250496c8aeSRafael J. Wysocki #define pm_genpd_suspend_late NULL 1326596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq NULL 13270496c8aeSRafael J. Wysocki #define pm_genpd_resume_early NULL 1328596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq NULL 1329596ba34bSRafael J. Wysocki #define pm_genpd_resume NULL 1330596ba34bSRafael J. Wysocki #define pm_genpd_freeze NULL 13310496c8aeSRafael J. Wysocki #define pm_genpd_freeze_late NULL 1332596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq NULL 13330496c8aeSRafael J. Wysocki #define pm_genpd_thaw_early NULL 1334596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq NULL 1335596ba34bSRafael J. Wysocki #define pm_genpd_thaw NULL 1336596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq NULL 1337596ba34bSRafael J. Wysocki #define pm_genpd_complete NULL 1338596ba34bSRafael J. Wysocki 1339596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1340596ba34bSRafael J. Wysocki 13411d5fcfecSRafael J. Wysocki static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) 13421d5fcfecSRafael J. Wysocki { 13431d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 13441d5fcfecSRafael J. Wysocki 13451d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 13461d5fcfecSRafael J. Wysocki if (!gpd_data) 13471d5fcfecSRafael J. Wysocki return NULL; 13481d5fcfecSRafael J. Wysocki 13491d5fcfecSRafael J. Wysocki mutex_init(&gpd_data->lock); 13501d5fcfecSRafael J. Wysocki gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 13511d5fcfecSRafael J. Wysocki dev_pm_qos_add_notifier(dev, &gpd_data->nb); 13521d5fcfecSRafael J. Wysocki return gpd_data; 13531d5fcfecSRafael J. Wysocki } 13541d5fcfecSRafael J. Wysocki 13551d5fcfecSRafael J. Wysocki static void __pm_genpd_free_dev_data(struct device *dev, 13561d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 13571d5fcfecSRafael J. Wysocki { 13581d5fcfecSRafael J. Wysocki dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 13591d5fcfecSRafael J. Wysocki kfree(gpd_data); 13601d5fcfecSRafael J. Wysocki } 13611d5fcfecSRafael J. Wysocki 1362f721889fSRafael J. Wysocki /** 1363b02c999aSRafael J. Wysocki * __pm_genpd_add_device - Add a device to an I/O PM domain. 1364f721889fSRafael J. Wysocki * @genpd: PM domain to add the device to. 1365f721889fSRafael J. Wysocki * @dev: Device to be added. 1366b02c999aSRafael J. Wysocki * @td: Set of PM QoS timing parameters to attach to the device. 1367f721889fSRafael J. Wysocki */ 1368b02c999aSRafael J. Wysocki int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1369b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1370f721889fSRafael J. Wysocki { 13711d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; 13724605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1373f721889fSRafael J. Wysocki int ret = 0; 1374f721889fSRafael J. Wysocki 1375f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1376f721889fSRafael J. Wysocki 1377f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1378f721889fSRafael J. Wysocki return -EINVAL; 1379f721889fSRafael J. Wysocki 13801d5fcfecSRafael J. Wysocki gpd_data_new = __pm_genpd_alloc_dev_data(dev); 13811d5fcfecSRafael J. Wysocki if (!gpd_data_new) 13826ff7bb0dSRafael J. Wysocki return -ENOMEM; 13836ff7bb0dSRafael J. Wysocki 138417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1385f721889fSRafael J. Wysocki 1386596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1387596ba34bSRafael J. Wysocki ret = -EAGAIN; 1388596ba34bSRafael J. Wysocki goto out; 1389596ba34bSRafael J. Wysocki } 1390596ba34bSRafael J. Wysocki 13914605ab65SRafael J. Wysocki list_for_each_entry(pdd, &genpd->dev_list, list_node) 13924605ab65SRafael J. Wysocki if (pdd->dev == dev) { 1393f721889fSRafael J. Wysocki ret = -EINVAL; 1394f721889fSRafael J. Wysocki goto out; 1395f721889fSRafael J. Wysocki } 1396f721889fSRafael J. Wysocki 13971d5fcfecSRafael J. Wysocki ret = dev_pm_get_subsys_data(dev); 13981d5fcfecSRafael J. Wysocki if (ret) 13991d5fcfecSRafael J. Wysocki goto out; 14001d5fcfecSRafael J. Wysocki 1401596ba34bSRafael J. Wysocki genpd->device_count++; 14026ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1403f721889fSRafael J. Wysocki 14046ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 14051d5fcfecSRafael J. Wysocki 14066ff7bb0dSRafael J. Wysocki dev->pm_domain = &genpd->domain; 14071d5fcfecSRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 14081d5fcfecSRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 14091d5fcfecSRafael J. Wysocki } else { 14101d5fcfecSRafael J. Wysocki gpd_data = gpd_data_new; 1411cd0ea672SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 14121d5fcfecSRafael J. Wysocki } 14131d5fcfecSRafael J. Wysocki gpd_data->refcount++; 1414b02c999aSRafael J. Wysocki if (td) 1415b02c999aSRafael J. Wysocki gpd_data->td = *td; 1416f721889fSRafael J. Wysocki 14171d5fcfecSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 14181d5fcfecSRafael J. Wysocki 14191d5fcfecSRafael J. Wysocki mutex_lock(&gpd_data->lock); 14201d5fcfecSRafael J. Wysocki gpd_data->base.dev = dev; 14211d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 14221d5fcfecSRafael J. Wysocki gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 14236ff7bb0dSRafael J. Wysocki gpd_data->td.constraint_changed = true; 14246ff7bb0dSRafael J. Wysocki gpd_data->td.effective_constraint_ns = -1; 14256ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 14266ff7bb0dSRafael J. Wysocki 1427f721889fSRafael J. Wysocki out: 142817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1429f721889fSRafael J. Wysocki 14301d5fcfecSRafael J. Wysocki if (gpd_data != gpd_data_new) 14311d5fcfecSRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data_new); 14321d5fcfecSRafael J. Wysocki 1433f721889fSRafael J. Wysocki return ret; 1434f721889fSRafael J. Wysocki } 1435f721889fSRafael J. Wysocki 1436f721889fSRafael J. Wysocki /** 1437c8aa130bSThomas Abraham * __pm_genpd_of_add_device - Add a device to an I/O PM domain. 1438c8aa130bSThomas Abraham * @genpd_node: Device tree node pointer representing a PM domain to which the 1439c8aa130bSThomas Abraham * the device is added to. 1440c8aa130bSThomas Abraham * @dev: Device to be added. 1441c8aa130bSThomas Abraham * @td: Set of PM QoS timing parameters to attach to the device. 1442c8aa130bSThomas Abraham */ 1443c8aa130bSThomas Abraham int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, 1444c8aa130bSThomas Abraham struct gpd_timing_data *td) 1445c8aa130bSThomas Abraham { 1446c8aa130bSThomas Abraham struct generic_pm_domain *genpd = NULL, *gpd; 1447c8aa130bSThomas Abraham 1448c8aa130bSThomas Abraham dev_dbg(dev, "%s()\n", __func__); 1449c8aa130bSThomas Abraham 1450c8aa130bSThomas Abraham if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) 1451c8aa130bSThomas Abraham return -EINVAL; 1452c8aa130bSThomas Abraham 1453c8aa130bSThomas Abraham mutex_lock(&gpd_list_lock); 1454c8aa130bSThomas Abraham list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 1455c8aa130bSThomas Abraham if (gpd->of_node == genpd_node) { 1456c8aa130bSThomas Abraham genpd = gpd; 1457c8aa130bSThomas Abraham break; 1458c8aa130bSThomas Abraham } 1459c8aa130bSThomas Abraham } 1460c8aa130bSThomas Abraham mutex_unlock(&gpd_list_lock); 1461c8aa130bSThomas Abraham 1462c8aa130bSThomas Abraham if (!genpd) 1463c8aa130bSThomas Abraham return -EINVAL; 1464c8aa130bSThomas Abraham 1465c8aa130bSThomas Abraham return __pm_genpd_add_device(genpd, dev, td); 1466c8aa130bSThomas Abraham } 1467c8aa130bSThomas Abraham 1468c8aa130bSThomas Abraham /** 1469f721889fSRafael J. Wysocki * pm_genpd_remove_device - Remove a device from an I/O PM domain. 1470f721889fSRafael J. Wysocki * @genpd: PM domain to remove the device from. 1471f721889fSRafael J. Wysocki * @dev: Device to be removed. 1472f721889fSRafael J. Wysocki */ 1473f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd, 1474f721889fSRafael J. Wysocki struct device *dev) 1475f721889fSRafael J. Wysocki { 14766ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 14774605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 14781d5fcfecSRafael J. Wysocki bool remove = false; 1479efa69025SRafael J. Wysocki int ret = 0; 1480f721889fSRafael J. Wysocki 1481f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1482f721889fSRafael J. Wysocki 1483efa69025SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) 1484efa69025SRafael J. Wysocki || IS_ERR_OR_NULL(dev->pm_domain) 1485efa69025SRafael J. Wysocki || pd_to_genpd(dev->pm_domain) != genpd) 1486f721889fSRafael J. Wysocki return -EINVAL; 1487f721889fSRafael J. Wysocki 148817b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1489f721889fSRafael J. Wysocki 1490596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1491596ba34bSRafael J. Wysocki ret = -EAGAIN; 1492596ba34bSRafael J. Wysocki goto out; 1493596ba34bSRafael J. Wysocki } 1494596ba34bSRafael J. Wysocki 14956ff7bb0dSRafael J. Wysocki genpd->device_count--; 14966ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 14976ff7bb0dSRafael J. Wysocki 14986ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 14991d5fcfecSRafael J. Wysocki 1500f721889fSRafael J. Wysocki dev->pm_domain = NULL; 1501efa69025SRafael J. Wysocki pdd = dev->power.subsys_data->domain_data; 1502efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 15031d5fcfecSRafael J. Wysocki gpd_data = to_gpd_data(pdd); 15041d5fcfecSRafael J. Wysocki if (--gpd_data->refcount == 0) { 1505efa69025SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 15061d5fcfecSRafael J. Wysocki remove = true; 15071d5fcfecSRafael J. Wysocki } 15081d5fcfecSRafael J. Wysocki 15096ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 1510f721889fSRafael J. Wysocki 15116ff7bb0dSRafael J. Wysocki mutex_lock(&gpd_data->lock); 15126ff7bb0dSRafael J. Wysocki pdd->dev = NULL; 15136ff7bb0dSRafael J. Wysocki mutex_unlock(&gpd_data->lock); 15146ff7bb0dSRafael J. Wysocki 15156ff7bb0dSRafael J. Wysocki genpd_release_lock(genpd); 15166ff7bb0dSRafael J. Wysocki 15176ff7bb0dSRafael J. Wysocki dev_pm_put_subsys_data(dev); 15181d5fcfecSRafael J. Wysocki if (remove) 15191d5fcfecSRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data); 15201d5fcfecSRafael J. Wysocki 15216ff7bb0dSRafael J. Wysocki return 0; 1522f721889fSRafael J. Wysocki 1523596ba34bSRafael J. Wysocki out: 152417b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1525f721889fSRafael J. Wysocki 1526f721889fSRafael J. Wysocki return ret; 1527f721889fSRafael J. Wysocki } 1528f721889fSRafael J. Wysocki 1529f721889fSRafael J. Wysocki /** 1530ca1d72f0SRafael J. Wysocki * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. 1531ca1d72f0SRafael J. Wysocki * @dev: Device to set/unset the flag for. 1532ca1d72f0SRafael J. Wysocki * @val: The new value of the device's "need restore" flag. 1533ca1d72f0SRafael J. Wysocki */ 1534ca1d72f0SRafael J. Wysocki void pm_genpd_dev_need_restore(struct device *dev, bool val) 1535ca1d72f0SRafael J. Wysocki { 1536ca1d72f0SRafael J. Wysocki struct pm_subsys_data *psd; 1537ca1d72f0SRafael J. Wysocki unsigned long flags; 1538ca1d72f0SRafael J. Wysocki 1539ca1d72f0SRafael J. Wysocki spin_lock_irqsave(&dev->power.lock, flags); 1540ca1d72f0SRafael J. Wysocki 1541ca1d72f0SRafael J. Wysocki psd = dev_to_psd(dev); 1542ca1d72f0SRafael J. Wysocki if (psd && psd->domain_data) 1543ca1d72f0SRafael J. Wysocki to_gpd_data(psd->domain_data)->need_restore = val; 1544ca1d72f0SRafael J. Wysocki 1545ca1d72f0SRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags); 1546ca1d72f0SRafael J. Wysocki } 1547ca1d72f0SRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); 1548ca1d72f0SRafael J. Wysocki 1549ca1d72f0SRafael J. Wysocki /** 1550f721889fSRafael J. Wysocki * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 1551f721889fSRafael J. Wysocki * @genpd: Master PM domain to add the subdomain to. 1552bc0403ffSRafael J. Wysocki * @subdomain: Subdomain to be added. 1553f721889fSRafael J. Wysocki */ 1554f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1555bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1556f721889fSRafael J. Wysocki { 15575063ce15SRafael J. Wysocki struct gpd_link *link; 1558f721889fSRafael J. Wysocki int ret = 0; 1559f721889fSRafael J. Wysocki 1560bc0403ffSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1561f721889fSRafael J. Wysocki return -EINVAL; 1562f721889fSRafael J. Wysocki 156317b75ecaSRafael J. Wysocki start: 156417b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1565bc0403ffSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1566f721889fSRafael J. Wysocki 1567bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 1568bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 1569bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 157017b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 157117b75ecaSRafael J. Wysocki goto start; 157217b75ecaSRafael J. Wysocki } 157317b75ecaSRafael J. Wysocki 157417b75ecaSRafael J. Wysocki if (genpd->status == GPD_STATE_POWER_OFF 1575bc0403ffSRafael J. Wysocki && subdomain->status != GPD_STATE_POWER_OFF) { 1576f721889fSRafael J. Wysocki ret = -EINVAL; 1577f721889fSRafael J. Wysocki goto out; 1578f721889fSRafael J. Wysocki } 1579f721889fSRafael J. Wysocki 15804fcac10dSHuang Ying list_for_each_entry(link, &genpd->master_links, master_node) { 1581bc0403ffSRafael J. Wysocki if (link->slave == subdomain && link->master == genpd) { 1582f721889fSRafael J. Wysocki ret = -EINVAL; 1583f721889fSRafael J. Wysocki goto out; 1584f721889fSRafael J. Wysocki } 1585f721889fSRafael J. Wysocki } 1586f721889fSRafael J. Wysocki 15875063ce15SRafael J. Wysocki link = kzalloc(sizeof(*link), GFP_KERNEL); 15885063ce15SRafael J. Wysocki if (!link) { 15895063ce15SRafael J. Wysocki ret = -ENOMEM; 15905063ce15SRafael J. Wysocki goto out; 15915063ce15SRafael J. Wysocki } 15925063ce15SRafael J. Wysocki link->master = genpd; 15935063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1594bc0403ffSRafael J. Wysocki link->slave = subdomain; 1595bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 1596bc0403ffSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1597c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1598f721889fSRafael J. Wysocki 1599f721889fSRafael J. Wysocki out: 1600bc0403ffSRafael J. Wysocki mutex_unlock(&subdomain->lock); 160117b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1602f721889fSRafael J. Wysocki 1603f721889fSRafael J. Wysocki return ret; 1604f721889fSRafael J. Wysocki } 1605f721889fSRafael J. Wysocki 1606f721889fSRafael J. Wysocki /** 1607f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1608f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 16095063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1610f721889fSRafael J. Wysocki */ 1611f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 16125063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1613f721889fSRafael J. Wysocki { 16145063ce15SRafael J. Wysocki struct gpd_link *link; 1615f721889fSRafael J. Wysocki int ret = -EINVAL; 1616f721889fSRafael J. Wysocki 16175063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1618f721889fSRafael J. Wysocki return -EINVAL; 1619f721889fSRafael J. Wysocki 162017b75ecaSRafael J. Wysocki start: 162117b75ecaSRafael J. Wysocki genpd_acquire_lock(genpd); 1622f721889fSRafael J. Wysocki 16235063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->master_links, master_node) { 16245063ce15SRafael J. Wysocki if (link->slave != subdomain) 1625f721889fSRafael J. Wysocki continue; 1626f721889fSRafael J. Wysocki 1627f721889fSRafael J. Wysocki mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1628f721889fSRafael J. Wysocki 162917b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF 163017b75ecaSRafael J. Wysocki && subdomain->status != GPD_STATE_ACTIVE) { 163117b75ecaSRafael J. Wysocki mutex_unlock(&subdomain->lock); 163217b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 163317b75ecaSRafael J. Wysocki goto start; 163417b75ecaSRafael J. Wysocki } 163517b75ecaSRafael J. Wysocki 16365063ce15SRafael J. Wysocki list_del(&link->master_node); 16375063ce15SRafael J. Wysocki list_del(&link->slave_node); 16385063ce15SRafael J. Wysocki kfree(link); 163917b75ecaSRafael J. Wysocki if (subdomain->status != GPD_STATE_POWER_OFF) 1640f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1641f721889fSRafael J. Wysocki 1642f721889fSRafael J. Wysocki mutex_unlock(&subdomain->lock); 1643f721889fSRafael J. Wysocki 1644f721889fSRafael J. Wysocki ret = 0; 1645f721889fSRafael J. Wysocki break; 1646f721889fSRafael J. Wysocki } 1647f721889fSRafael J. Wysocki 164817b75ecaSRafael J. Wysocki genpd_release_lock(genpd); 1649f721889fSRafael J. Wysocki 1650f721889fSRafael J. Wysocki return ret; 1651f721889fSRafael J. Wysocki } 1652f721889fSRafael J. Wysocki 1653f721889fSRafael J. Wysocki /** 1654d5e4cbfeSRafael J. Wysocki * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. 1655d5e4cbfeSRafael J. Wysocki * @dev: Device to add the callbacks to. 1656d5e4cbfeSRafael J. Wysocki * @ops: Set of callbacks to add. 1657b02c999aSRafael J. Wysocki * @td: Timing data to add to the device along with the callbacks (optional). 165862d44902SRafael J. Wysocki * 165962d44902SRafael J. Wysocki * Every call to this routine should be balanced with a call to 166062d44902SRafael J. Wysocki * __pm_genpd_remove_callbacks() and they must not be nested. 1661d5e4cbfeSRafael J. Wysocki */ 1662b02c999aSRafael J. Wysocki int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, 1663b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1664d5e4cbfeSRafael J. Wysocki { 166562d44902SRafael J. Wysocki struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; 1666d5e4cbfeSRafael J. Wysocki int ret = 0; 1667d5e4cbfeSRafael J. Wysocki 166862d44902SRafael J. Wysocki if (!(dev && ops)) 1669d5e4cbfeSRafael J. Wysocki return -EINVAL; 1670d5e4cbfeSRafael J. Wysocki 167162d44902SRafael J. Wysocki gpd_data_new = __pm_genpd_alloc_dev_data(dev); 167262d44902SRafael J. Wysocki if (!gpd_data_new) 167362d44902SRafael J. Wysocki return -ENOMEM; 167462d44902SRafael J. Wysocki 1675d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1676d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1677d5e4cbfeSRafael J. Wysocki 167862d44902SRafael J. Wysocki ret = dev_pm_get_subsys_data(dev); 167962d44902SRafael J. Wysocki if (ret) 168062d44902SRafael J. Wysocki goto out; 1681d5e4cbfeSRafael J. Wysocki 168262d44902SRafael J. Wysocki spin_lock_irq(&dev->power.lock); 168362d44902SRafael J. Wysocki 168462d44902SRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 168562d44902SRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 168662d44902SRafael J. Wysocki } else { 168762d44902SRafael J. Wysocki gpd_data = gpd_data_new; 168862d44902SRafael J. Wysocki dev->power.subsys_data->domain_data = &gpd_data->base; 168962d44902SRafael J. Wysocki } 169062d44902SRafael J. Wysocki gpd_data->refcount++; 1691d5e4cbfeSRafael J. Wysocki gpd_data->ops = *ops; 1692b02c999aSRafael J. Wysocki if (td) 1693b02c999aSRafael J. Wysocki gpd_data->td = *td; 1694d5e4cbfeSRafael J. Wysocki 169562d44902SRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 169662d44902SRafael J. Wysocki 169762d44902SRafael J. Wysocki out: 1698d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1699d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1700d5e4cbfeSRafael J. Wysocki 170162d44902SRafael J. Wysocki if (gpd_data != gpd_data_new) 170262d44902SRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data_new); 170362d44902SRafael J. Wysocki 1704d5e4cbfeSRafael J. Wysocki return ret; 1705d5e4cbfeSRafael J. Wysocki } 1706d5e4cbfeSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); 1707d5e4cbfeSRafael J. Wysocki 1708d5e4cbfeSRafael J. Wysocki /** 1709b02c999aSRafael J. Wysocki * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. 1710d5e4cbfeSRafael J. Wysocki * @dev: Device to remove the callbacks from. 1711b02c999aSRafael J. Wysocki * @clear_td: If set, clear the device's timing data too. 171262d44902SRafael J. Wysocki * 171362d44902SRafael J. Wysocki * This routine can only be called after pm_genpd_add_callbacks(). 1714d5e4cbfeSRafael J. Wysocki */ 1715b02c999aSRafael J. Wysocki int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) 1716d5e4cbfeSRafael J. Wysocki { 171762d44902SRafael J. Wysocki struct generic_pm_domain_data *gpd_data = NULL; 171862d44902SRafael J. Wysocki bool remove = false; 1719d5e4cbfeSRafael J. Wysocki int ret = 0; 1720d5e4cbfeSRafael J. Wysocki 1721d5e4cbfeSRafael J. Wysocki if (!(dev && dev->power.subsys_data)) 1722d5e4cbfeSRafael J. Wysocki return -EINVAL; 1723d5e4cbfeSRafael J. Wysocki 1724d5e4cbfeSRafael J. Wysocki pm_runtime_disable(dev); 1725d5e4cbfeSRafael J. Wysocki device_pm_lock(); 1726d5e4cbfeSRafael J. Wysocki 172762d44902SRafael J. Wysocki spin_lock_irq(&dev->power.lock); 1728d5e4cbfeSRafael J. Wysocki 172962d44902SRafael J. Wysocki if (dev->power.subsys_data->domain_data) { 173062d44902SRafael J. Wysocki gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 1731db79e53dSSachin Kamat gpd_data->ops = (struct gpd_dev_ops){ NULL }; 1732b02c999aSRafael J. Wysocki if (clear_td) 1733b02c999aSRafael J. Wysocki gpd_data->td = (struct gpd_timing_data){ 0 }; 173462d44902SRafael J. Wysocki 173562d44902SRafael J. Wysocki if (--gpd_data->refcount == 0) { 173662d44902SRafael J. Wysocki dev->power.subsys_data->domain_data = NULL; 173762d44902SRafael J. Wysocki remove = true; 173862d44902SRafael J. Wysocki } 1739d5e4cbfeSRafael J. Wysocki } else { 1740d5e4cbfeSRafael J. Wysocki ret = -EINVAL; 1741d5e4cbfeSRafael J. Wysocki } 1742d5e4cbfeSRafael J. Wysocki 174362d44902SRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 174462d44902SRafael J. Wysocki 1745d5e4cbfeSRafael J. Wysocki device_pm_unlock(); 1746d5e4cbfeSRafael J. Wysocki pm_runtime_enable(dev); 1747d5e4cbfeSRafael J. Wysocki 174862d44902SRafael J. Wysocki if (ret) 1749d5e4cbfeSRafael J. Wysocki return ret; 175062d44902SRafael J. Wysocki 175162d44902SRafael J. Wysocki dev_pm_put_subsys_data(dev); 175262d44902SRafael J. Wysocki if (remove) 175362d44902SRafael J. Wysocki __pm_genpd_free_dev_data(dev, gpd_data); 175462d44902SRafael J. Wysocki 175562d44902SRafael J. Wysocki return 0; 1756d5e4cbfeSRafael J. Wysocki } 1757b02c999aSRafael J. Wysocki EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); 1758d5e4cbfeSRafael J. Wysocki 1759cbc9ef02SRafael J. Wysocki int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) 1760cbc9ef02SRafael J. Wysocki { 1761cbc9ef02SRafael J. Wysocki struct cpuidle_driver *cpuidle_drv; 1762cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1763cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1764cbc9ef02SRafael J. Wysocki int ret = 0; 1765cbc9ef02SRafael J. Wysocki 1766cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || state < 0) 1767cbc9ef02SRafael J. Wysocki return -EINVAL; 1768cbc9ef02SRafael J. Wysocki 1769cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1770cbc9ef02SRafael J. Wysocki 1771cbc9ef02SRafael J. Wysocki if (genpd->cpu_data) { 1772cbc9ef02SRafael J. Wysocki ret = -EEXIST; 1773cbc9ef02SRafael J. Wysocki goto out; 1774cbc9ef02SRafael J. Wysocki } 1775cbc9ef02SRafael J. Wysocki cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); 1776cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1777cbc9ef02SRafael J. Wysocki ret = -ENOMEM; 1778cbc9ef02SRafael J. Wysocki goto out; 1779cbc9ef02SRafael J. Wysocki } 1780cbc9ef02SRafael J. Wysocki cpuidle_drv = cpuidle_driver_ref(); 1781cbc9ef02SRafael J. Wysocki if (!cpuidle_drv) { 1782cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1783cbc9ef02SRafael J. Wysocki goto out; 1784cbc9ef02SRafael J. Wysocki } 1785cbc9ef02SRafael J. Wysocki if (cpuidle_drv->state_count <= state) { 1786cbc9ef02SRafael J. Wysocki ret = -EINVAL; 1787cbc9ef02SRafael J. Wysocki goto err; 1788cbc9ef02SRafael J. Wysocki } 1789cbc9ef02SRafael J. Wysocki idle_state = &cpuidle_drv->states[state]; 1790cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1791cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1792cbc9ef02SRafael J. Wysocki goto err; 1793cbc9ef02SRafael J. Wysocki } 1794cbc9ef02SRafael J. Wysocki cpu_data->idle_state = idle_state; 1795cbc9ef02SRafael J. Wysocki cpu_data->saved_exit_latency = idle_state->exit_latency; 1796cbc9ef02SRafael J. Wysocki genpd->cpu_data = cpu_data; 1797cbc9ef02SRafael J. Wysocki genpd_recalc_cpu_exit_latency(genpd); 1798cbc9ef02SRafael J. Wysocki 1799cbc9ef02SRafael J. Wysocki out: 1800cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1801cbc9ef02SRafael J. Wysocki return ret; 1802cbc9ef02SRafael J. Wysocki 1803cbc9ef02SRafael J. Wysocki err: 1804cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1805cbc9ef02SRafael J. Wysocki goto out; 1806cbc9ef02SRafael J. Wysocki } 1807cbc9ef02SRafael J. Wysocki 1808cbc9ef02SRafael J. Wysocki int genpd_detach_cpuidle(struct generic_pm_domain *genpd) 1809cbc9ef02SRafael J. Wysocki { 1810cbc9ef02SRafael J. Wysocki struct gpd_cpu_data *cpu_data; 1811cbc9ef02SRafael J. Wysocki struct cpuidle_state *idle_state; 1812cbc9ef02SRafael J. Wysocki int ret = 0; 1813cbc9ef02SRafael J. Wysocki 1814cbc9ef02SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 1815cbc9ef02SRafael J. Wysocki return -EINVAL; 1816cbc9ef02SRafael J. Wysocki 1817cbc9ef02SRafael J. Wysocki genpd_acquire_lock(genpd); 1818cbc9ef02SRafael J. Wysocki 1819cbc9ef02SRafael J. Wysocki cpu_data = genpd->cpu_data; 1820cbc9ef02SRafael J. Wysocki if (!cpu_data) { 1821cbc9ef02SRafael J. Wysocki ret = -ENODEV; 1822cbc9ef02SRafael J. Wysocki goto out; 1823cbc9ef02SRafael J. Wysocki } 1824cbc9ef02SRafael J. Wysocki idle_state = cpu_data->idle_state; 1825cbc9ef02SRafael J. Wysocki if (!idle_state->disabled) { 1826cbc9ef02SRafael J. Wysocki ret = -EAGAIN; 1827cbc9ef02SRafael J. Wysocki goto out; 1828cbc9ef02SRafael J. Wysocki } 1829cbc9ef02SRafael J. Wysocki idle_state->exit_latency = cpu_data->saved_exit_latency; 1830cbc9ef02SRafael J. Wysocki cpuidle_driver_unref(); 1831cbc9ef02SRafael J. Wysocki genpd->cpu_data = NULL; 1832cbc9ef02SRafael J. Wysocki kfree(cpu_data); 1833cbc9ef02SRafael J. Wysocki 1834cbc9ef02SRafael J. Wysocki out: 1835cbc9ef02SRafael J. Wysocki genpd_release_lock(genpd); 1836cbc9ef02SRafael J. Wysocki return ret; 1837cbc9ef02SRafael J. Wysocki } 1838cbc9ef02SRafael J. Wysocki 1839d23b9b00SRafael J. Wysocki /* Default device callbacks for generic PM domains. */ 1840d23b9b00SRafael J. Wysocki 1841d5e4cbfeSRafael J. Wysocki /** 1842ecf00475SRafael J. Wysocki * pm_genpd_default_save_state - Default "save device state" for PM domians. 1843ecf00475SRafael J. Wysocki * @dev: Device to handle. 1844ecf00475SRafael J. Wysocki */ 1845ecf00475SRafael J. Wysocki static int pm_genpd_default_save_state(struct device *dev) 1846ecf00475SRafael J. Wysocki { 1847ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1848ecf00475SRafael J. Wysocki 1849ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.save_state; 1850ecf00475SRafael J. Wysocki if (cb) 1851ecf00475SRafael J. Wysocki return cb(dev); 1852ecf00475SRafael J. Wysocki 18530b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 18540b589741SRafael J. Wysocki cb = dev->type->pm->runtime_suspend; 18550b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 18560b589741SRafael J. Wysocki cb = dev->class->pm->runtime_suspend; 18570b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 18580b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_suspend; 18590b589741SRafael J. Wysocki else 18600b589741SRafael J. Wysocki cb = NULL; 1861ecf00475SRafael J. Wysocki 18620b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 18630b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_suspend; 18640b589741SRafael J. Wysocki 18650b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1866ecf00475SRafael J. Wysocki } 1867ecf00475SRafael J. Wysocki 1868ecf00475SRafael J. Wysocki /** 1869ecf00475SRafael J. Wysocki * pm_genpd_default_restore_state - Default PM domians "restore device state". 1870ecf00475SRafael J. Wysocki * @dev: Device to handle. 1871ecf00475SRafael J. Wysocki */ 1872ecf00475SRafael J. Wysocki static int pm_genpd_default_restore_state(struct device *dev) 1873ecf00475SRafael J. Wysocki { 1874ecf00475SRafael J. Wysocki int (*cb)(struct device *__dev); 1875ecf00475SRafael J. Wysocki 1876ecf00475SRafael J. Wysocki cb = dev_gpd_data(dev)->ops.restore_state; 1877ecf00475SRafael J. Wysocki if (cb) 1878ecf00475SRafael J. Wysocki return cb(dev); 1879ecf00475SRafael J. Wysocki 18800b589741SRafael J. Wysocki if (dev->type && dev->type->pm) 18810b589741SRafael J. Wysocki cb = dev->type->pm->runtime_resume; 18820b589741SRafael J. Wysocki else if (dev->class && dev->class->pm) 18830b589741SRafael J. Wysocki cb = dev->class->pm->runtime_resume; 18840b589741SRafael J. Wysocki else if (dev->bus && dev->bus->pm) 18850b589741SRafael J. Wysocki cb = dev->bus->pm->runtime_resume; 18860b589741SRafael J. Wysocki else 18870b589741SRafael J. Wysocki cb = NULL; 1888ecf00475SRafael J. Wysocki 18890b589741SRafael J. Wysocki if (!cb && dev->driver && dev->driver->pm) 18900b589741SRafael J. Wysocki cb = dev->driver->pm->runtime_resume; 18910b589741SRafael J. Wysocki 18920b589741SRafael J. Wysocki return cb ? cb(dev) : 0; 1893ecf00475SRafael J. Wysocki } 1894ecf00475SRafael J. Wysocki 18950f1d6986SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 18960f1d6986SRafael J. Wysocki 1897ecf00475SRafael J. Wysocki /** 1898d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend - Default "device suspend" for PM domians. 1899d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1900d23b9b00SRafael J. Wysocki */ 1901d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend(struct device *dev) 1902d23b9b00SRafael J. Wysocki { 1903c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend; 1904d23b9b00SRafael J. Wysocki 1905d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend(dev); 1906d23b9b00SRafael J. Wysocki } 1907d23b9b00SRafael J. Wysocki 1908d23b9b00SRafael J. Wysocki /** 1909d23b9b00SRafael J. Wysocki * pm_genpd_default_suspend_late - Default "late device suspend" for PM domians. 1910d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1911d23b9b00SRafael J. Wysocki */ 1912d23b9b00SRafael J. Wysocki static int pm_genpd_default_suspend_late(struct device *dev) 1913d23b9b00SRafael J. Wysocki { 1914c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; 1915d23b9b00SRafael J. Wysocki 19160496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_suspend_late(dev); 1917d23b9b00SRafael J. Wysocki } 1918d23b9b00SRafael J. Wysocki 1919d23b9b00SRafael J. Wysocki /** 1920d23b9b00SRafael J. Wysocki * pm_genpd_default_resume_early - Default "early device resume" for PM domians. 1921d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1922d23b9b00SRafael J. Wysocki */ 1923d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume_early(struct device *dev) 1924d23b9b00SRafael J. Wysocki { 1925c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; 1926d23b9b00SRafael J. Wysocki 19270496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume_early(dev); 1928d23b9b00SRafael J. Wysocki } 1929d23b9b00SRafael J. Wysocki 1930d23b9b00SRafael J. Wysocki /** 1931d23b9b00SRafael J. Wysocki * pm_genpd_default_resume - Default "device resume" for PM domians. 1932d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1933d23b9b00SRafael J. Wysocki */ 1934d23b9b00SRafael J. Wysocki static int pm_genpd_default_resume(struct device *dev) 1935d23b9b00SRafael J. Wysocki { 1936c9914854SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume; 1937d23b9b00SRafael J. Wysocki 1938d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_resume(dev); 1939d23b9b00SRafael J. Wysocki } 1940d23b9b00SRafael J. Wysocki 1941d23b9b00SRafael J. Wysocki /** 1942d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze - Default "device freeze" for PM domians. 1943d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1944d23b9b00SRafael J. Wysocki */ 1945d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze(struct device *dev) 1946d23b9b00SRafael J. Wysocki { 1947d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze; 1948d23b9b00SRafael J. Wysocki 1949d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze(dev); 1950d23b9b00SRafael J. Wysocki } 1951d23b9b00SRafael J. Wysocki 1952d23b9b00SRafael J. Wysocki /** 1953d23b9b00SRafael J. Wysocki * pm_genpd_default_freeze_late - Default "late device freeze" for PM domians. 1954d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1955d23b9b00SRafael J. Wysocki */ 1956d23b9b00SRafael J. Wysocki static int pm_genpd_default_freeze_late(struct device *dev) 1957d23b9b00SRafael J. Wysocki { 1958d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; 1959d23b9b00SRafael J. Wysocki 19600496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_freeze_late(dev); 1961d23b9b00SRafael J. Wysocki } 1962d23b9b00SRafael J. Wysocki 1963d23b9b00SRafael J. Wysocki /** 1964d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw_early - Default "early device thaw" for PM domians. 1965d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1966d23b9b00SRafael J. Wysocki */ 1967d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw_early(struct device *dev) 1968d23b9b00SRafael J. Wysocki { 1969d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; 1970d23b9b00SRafael J. Wysocki 19710496c8aeSRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw_early(dev); 1972d23b9b00SRafael J. Wysocki } 1973d23b9b00SRafael J. Wysocki 1974d23b9b00SRafael J. Wysocki /** 1975d23b9b00SRafael J. Wysocki * pm_genpd_default_thaw - Default "device thaw" for PM domians. 1976d23b9b00SRafael J. Wysocki * @dev: Device to handle. 1977d23b9b00SRafael J. Wysocki */ 1978d23b9b00SRafael J. Wysocki static int pm_genpd_default_thaw(struct device *dev) 1979d23b9b00SRafael J. Wysocki { 1980d23b9b00SRafael J. Wysocki int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw; 1981d23b9b00SRafael J. Wysocki 1982d23b9b00SRafael J. Wysocki return cb ? cb(dev) : pm_generic_thaw(dev); 1983d23b9b00SRafael J. Wysocki } 1984d23b9b00SRafael J. Wysocki 19850f1d6986SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 19860f1d6986SRafael J. Wysocki 19870f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend NULL 19880f1d6986SRafael J. Wysocki #define pm_genpd_default_suspend_late NULL 19890f1d6986SRafael J. Wysocki #define pm_genpd_default_resume_early NULL 19900f1d6986SRafael J. Wysocki #define pm_genpd_default_resume NULL 19910f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze NULL 19920f1d6986SRafael J. Wysocki #define pm_genpd_default_freeze_late NULL 19930f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw_early NULL 19940f1d6986SRafael J. Wysocki #define pm_genpd_default_thaw NULL 19950f1d6986SRafael J. Wysocki 19960f1d6986SRafael J. Wysocki #endif /* !CONFIG_PM_SLEEP */ 19970f1d6986SRafael J. Wysocki 1998d23b9b00SRafael J. Wysocki /** 1999f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 2000f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 2001f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 2002f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 2003f721889fSRafael J. Wysocki */ 2004f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd, 2005f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 2006f721889fSRafael J. Wysocki { 2007f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 2008f721889fSRafael J. Wysocki return; 2009f721889fSRafael J. Wysocki 20105063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 20115063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 2012f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 2013f721889fSRafael J. Wysocki mutex_init(&genpd->lock); 2014f721889fSRafael J. Wysocki genpd->gov = gov; 2015f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 2016f721889fSRafael J. Wysocki genpd->in_progress = 0; 2017c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 201817b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 201917b75ecaSRafael J. Wysocki init_waitqueue_head(&genpd->status_wait_queue); 2020c6d22b37SRafael J. Wysocki genpd->poweroff_task = NULL; 2021c6d22b37SRafael J. Wysocki genpd->resume_count = 0; 2022596ba34bSRafael J. Wysocki genpd->device_count = 0; 2023221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 20246ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 2025f721889fSRafael J. Wysocki genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; 2026f721889fSRafael J. Wysocki genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; 2027f721889fSRafael J. Wysocki genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; 2028596ba34bSRafael J. Wysocki genpd->domain.ops.prepare = pm_genpd_prepare; 2029596ba34bSRafael J. Wysocki genpd->domain.ops.suspend = pm_genpd_suspend; 20300496c8aeSRafael J. Wysocki genpd->domain.ops.suspend_late = pm_genpd_suspend_late; 2031596ba34bSRafael J. Wysocki genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; 2032596ba34bSRafael J. Wysocki genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; 20330496c8aeSRafael J. Wysocki genpd->domain.ops.resume_early = pm_genpd_resume_early; 2034596ba34bSRafael J. Wysocki genpd->domain.ops.resume = pm_genpd_resume; 2035596ba34bSRafael J. Wysocki genpd->domain.ops.freeze = pm_genpd_freeze; 20360496c8aeSRafael J. Wysocki genpd->domain.ops.freeze_late = pm_genpd_freeze_late; 2037596ba34bSRafael J. Wysocki genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; 2038596ba34bSRafael J. Wysocki genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; 20390496c8aeSRafael J. Wysocki genpd->domain.ops.thaw_early = pm_genpd_thaw_early; 2040596ba34bSRafael J. Wysocki genpd->domain.ops.thaw = pm_genpd_thaw; 2041d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff = pm_genpd_suspend; 20420496c8aeSRafael J. Wysocki genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; 2043d23b9b00SRafael J. Wysocki genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; 2044596ba34bSRafael J. Wysocki genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; 20450496c8aeSRafael J. Wysocki genpd->domain.ops.restore_early = pm_genpd_resume_early; 2046d23b9b00SRafael J. Wysocki genpd->domain.ops.restore = pm_genpd_resume; 2047596ba34bSRafael J. Wysocki genpd->domain.ops.complete = pm_genpd_complete; 2048ecf00475SRafael J. Wysocki genpd->dev_ops.save_state = pm_genpd_default_save_state; 2049ecf00475SRafael J. Wysocki genpd->dev_ops.restore_state = pm_genpd_default_restore_state; 2050c9914854SRafael J. Wysocki genpd->dev_ops.suspend = pm_genpd_default_suspend; 2051c9914854SRafael J. Wysocki genpd->dev_ops.suspend_late = pm_genpd_default_suspend_late; 2052c9914854SRafael J. Wysocki genpd->dev_ops.resume_early = pm_genpd_default_resume_early; 2053c9914854SRafael J. Wysocki genpd->dev_ops.resume = pm_genpd_default_resume; 2054d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze = pm_genpd_default_freeze; 2055d23b9b00SRafael J. Wysocki genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late; 2056d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early; 2057d23b9b00SRafael J. Wysocki genpd->dev_ops.thaw = pm_genpd_default_thaw; 20585125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 20595125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 20605125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 20615125bbf3SRafael J. Wysocki } 2062