162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/base/power/domain.c - Common code related to device power domains. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define pr_fmt(fmt) "PM: " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/pm_opp.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/pm_domain.h> 1662306a36Sopenharmony_ci#include <linux/pm_qos.h> 1762306a36Sopenharmony_ci#include <linux/pm_clock.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/sched.h> 2162306a36Sopenharmony_ci#include <linux/suspend.h> 2262306a36Sopenharmony_ci#include <linux/export.h> 2362306a36Sopenharmony_ci#include <linux/cpu.h> 2462306a36Sopenharmony_ci#include <linux/debugfs.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "power.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define GENPD_RETRY_MAX_MS 250 /* Approximate */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 3162306a36Sopenharmony_ci({ \ 3262306a36Sopenharmony_ci type (*__routine)(struct device *__d); \ 3362306a36Sopenharmony_ci type __ret = (type)0; \ 3462306a36Sopenharmony_ci \ 3562306a36Sopenharmony_ci __routine = genpd->dev_ops.callback; \ 3662306a36Sopenharmony_ci if (__routine) { \ 3762306a36Sopenharmony_ci __ret = __routine(dev); \ 3862306a36Sopenharmony_ci } \ 3962306a36Sopenharmony_ci __ret; \ 4062306a36Sopenharmony_ci}) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic LIST_HEAD(gpd_list); 4362306a36Sopenharmony_cistatic DEFINE_MUTEX(gpd_list_lock); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct genpd_lock_ops { 4662306a36Sopenharmony_ci void (*lock)(struct generic_pm_domain *genpd); 4762306a36Sopenharmony_ci void (*lock_nested)(struct generic_pm_domain *genpd, int depth); 4862306a36Sopenharmony_ci int (*lock_interruptible)(struct generic_pm_domain *genpd); 4962306a36Sopenharmony_ci void (*unlock)(struct generic_pm_domain *genpd); 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void genpd_lock_mtx(struct generic_pm_domain *genpd) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci mutex_lock(&genpd->mlock); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void genpd_lock_nested_mtx(struct generic_pm_domain *genpd, 5862306a36Sopenharmony_ci int depth) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci mutex_lock_nested(&genpd->mlock, depth); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return mutex_lock_interruptible(&genpd->mlock); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void genpd_unlock_mtx(struct generic_pm_domain *genpd) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return mutex_unlock(&genpd->mlock); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct genpd_lock_ops genpd_mtx_ops = { 7462306a36Sopenharmony_ci .lock = genpd_lock_mtx, 7562306a36Sopenharmony_ci .lock_nested = genpd_lock_nested_mtx, 7662306a36Sopenharmony_ci .lock_interruptible = genpd_lock_interruptible_mtx, 7762306a36Sopenharmony_ci .unlock = genpd_unlock_mtx, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void genpd_lock_spin(struct generic_pm_domain *genpd) 8162306a36Sopenharmony_ci __acquires(&genpd->slock) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci unsigned long flags; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci spin_lock_irqsave(&genpd->slock, flags); 8662306a36Sopenharmony_ci genpd->lock_flags = flags; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void genpd_lock_nested_spin(struct generic_pm_domain *genpd, 9062306a36Sopenharmony_ci int depth) 9162306a36Sopenharmony_ci __acquires(&genpd->slock) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned long flags; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci spin_lock_irqsave_nested(&genpd->slock, flags, depth); 9662306a36Sopenharmony_ci genpd->lock_flags = flags; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd) 10062306a36Sopenharmony_ci __acquires(&genpd->slock) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci spin_lock_irqsave(&genpd->slock, flags); 10562306a36Sopenharmony_ci genpd->lock_flags = flags; 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void genpd_unlock_spin(struct generic_pm_domain *genpd) 11062306a36Sopenharmony_ci __releases(&genpd->slock) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct genpd_lock_ops genpd_spin_ops = { 11662306a36Sopenharmony_ci .lock = genpd_lock_spin, 11762306a36Sopenharmony_ci .lock_nested = genpd_lock_nested_spin, 11862306a36Sopenharmony_ci .lock_interruptible = genpd_lock_interruptible_spin, 11962306a36Sopenharmony_ci .unlock = genpd_unlock_spin, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define genpd_lock(p) p->lock_ops->lock(p) 12362306a36Sopenharmony_ci#define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d) 12462306a36Sopenharmony_ci#define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p) 12562306a36Sopenharmony_ci#define genpd_unlock(p) p->lock_ops->unlock(p) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON) 12862306a36Sopenharmony_ci#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) 12962306a36Sopenharmony_ci#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) 13062306a36Sopenharmony_ci#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) 13162306a36Sopenharmony_ci#define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN) 13262306a36Sopenharmony_ci#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic inline bool irq_safe_dev_in_sleep_domain(struct device *dev, 13562306a36Sopenharmony_ci const struct generic_pm_domain *genpd) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci bool ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Warn once if an IRQ safe device is attached to a domain, which 14362306a36Sopenharmony_ci * callbacks are allowed to sleep. This indicates a suboptimal 14462306a36Sopenharmony_ci * configuration for PM, but it doesn't matter for an always on domain. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci if (genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci dev_warn_once(dev, "PM domain %s will not be powered off\n", 15162306a36Sopenharmony_ci genpd->name); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int genpd_runtime_suspend(struct device *dev); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * Get the generic PM domain for a particular struct device. 16062306a36Sopenharmony_ci * This validates the struct device pointer, the PM domain pointer, 16162306a36Sopenharmony_ci * and checks that the PM domain pointer is a real generic PM domain. 16262306a36Sopenharmony_ci * Any failure results in NULL being returned. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic struct generic_pm_domain *dev_to_genpd_safe(struct device *dev) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 16762306a36Sopenharmony_ci return NULL; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* A genpd's always have its ->runtime_suspend() callback assigned. */ 17062306a36Sopenharmony_ci if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend) 17162306a36Sopenharmony_ci return pd_to_genpd(dev->pm_domain); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return NULL; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * This should only be used where we are certain that the pm_domain 17862306a36Sopenharmony_ci * attached to the device is a genpd domain. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic struct generic_pm_domain *dev_to_genpd(struct device *dev) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dev->pm_domain)) 18362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return pd_to_genpd(dev->pm_domain); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int genpd_stop_dev(const struct generic_pm_domain *genpd, 18962306a36Sopenharmony_ci struct device *dev) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int genpd_start_dev(const struct generic_pm_domain *genpd, 19562306a36Sopenharmony_ci struct device *dev) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return GENPD_DEV_CALLBACK(genpd, int, start, dev); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci bool ret = false; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 20562306a36Sopenharmony_ci ret = !!atomic_dec_and_test(&genpd->sd_count); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci atomic_inc(&genpd->sd_count); 21362306a36Sopenharmony_ci smp_mb__after_atomic(); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 21762306a36Sopenharmony_cistatic struct dentry *genpd_debugfs_dir; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void genpd_debug_add(struct generic_pm_domain *genpd); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void genpd_debug_remove(struct generic_pm_domain *genpd) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci if (!genpd_debugfs_dir) 22462306a36Sopenharmony_ci return; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci debugfs_lookup_and_remove(genpd->name, genpd_debugfs_dir); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void genpd_update_accounting(struct generic_pm_domain *genpd) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci u64 delta, now; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci now = ktime_get_mono_fast_ns(); 23462306a36Sopenharmony_ci if (now <= genpd->accounting_time) 23562306a36Sopenharmony_ci return; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci delta = now - genpd->accounting_time; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * If genpd->status is active, it means we are just 24162306a36Sopenharmony_ci * out of off and so update the idle time and vice 24262306a36Sopenharmony_ci * versa. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (genpd->status == GENPD_STATE_ON) 24562306a36Sopenharmony_ci genpd->states[genpd->state_idx].idle_time += delta; 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci genpd->on_time += delta; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci genpd->accounting_time = now; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci#else 25262306a36Sopenharmony_cistatic inline void genpd_debug_add(struct generic_pm_domain *genpd) {} 25362306a36Sopenharmony_cistatic inline void genpd_debug_remove(struct generic_pm_domain *genpd) {} 25462306a36Sopenharmony_cistatic inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} 25562306a36Sopenharmony_ci#endif 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, 25862306a36Sopenharmony_ci unsigned int state) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct generic_pm_domain_data *pd_data; 26162306a36Sopenharmony_ci struct pm_domain_data *pdd; 26262306a36Sopenharmony_ci struct gpd_link *link; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* New requested state is same as Max requested state */ 26562306a36Sopenharmony_ci if (state == genpd->performance_state) 26662306a36Sopenharmony_ci return state; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* New requested state is higher than Max requested state */ 26962306a36Sopenharmony_ci if (state > genpd->performance_state) 27062306a36Sopenharmony_ci return state; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Traverse all devices within the domain */ 27362306a36Sopenharmony_ci list_for_each_entry(pdd, &genpd->dev_list, list_node) { 27462306a36Sopenharmony_ci pd_data = to_gpd_data(pdd); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (pd_data->performance_state > state) 27762306a36Sopenharmony_ci state = pd_data->performance_state; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * Traverse all sub-domains within the domain. This can be 28262306a36Sopenharmony_ci * done without any additional locking as the link->performance_state 28362306a36Sopenharmony_ci * field is protected by the parent genpd->lock, which is already taken. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * Also note that link->performance_state (subdomain's performance state 28662306a36Sopenharmony_ci * requirement to parent domain) is different from 28762306a36Sopenharmony_ci * link->child->performance_state (current performance state requirement 28862306a36Sopenharmony_ci * of the devices/sub-domains of the subdomain) and so can have a 28962306a36Sopenharmony_ci * different value. 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * Note that we also take vote from powered-off sub-domains into account 29262306a36Sopenharmony_ci * as the same is done for devices right now. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci list_for_each_entry(link, &genpd->parent_links, parent_node) { 29562306a36Sopenharmony_ci if (link->performance_state > state) 29662306a36Sopenharmony_ci state = link->performance_state; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return state; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int genpd_xlate_performance_state(struct generic_pm_domain *genpd, 30362306a36Sopenharmony_ci struct generic_pm_domain *parent, 30462306a36Sopenharmony_ci unsigned int pstate) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci if (!parent->set_performance_state) 30762306a36Sopenharmony_ci return pstate; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return dev_pm_opp_xlate_performance_state(genpd->opp_table, 31062306a36Sopenharmony_ci parent->opp_table, 31162306a36Sopenharmony_ci pstate); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int _genpd_set_performance_state(struct generic_pm_domain *genpd, 31562306a36Sopenharmony_ci unsigned int state, int depth) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct generic_pm_domain *parent; 31862306a36Sopenharmony_ci struct gpd_link *link; 31962306a36Sopenharmony_ci int parent_state, ret; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (state == genpd->performance_state) 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Propagate to parents of genpd */ 32562306a36Sopenharmony_ci list_for_each_entry(link, &genpd->child_links, child_node) { 32662306a36Sopenharmony_ci parent = link->parent; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Find parent's performance state */ 32962306a36Sopenharmony_ci ret = genpd_xlate_performance_state(genpd, parent, state); 33062306a36Sopenharmony_ci if (unlikely(ret < 0)) 33162306a36Sopenharmony_ci goto err; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci parent_state = ret; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci genpd_lock_nested(parent, depth + 1); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci link->prev_performance_state = link->performance_state; 33862306a36Sopenharmony_ci link->performance_state = parent_state; 33962306a36Sopenharmony_ci parent_state = _genpd_reeval_performance_state(parent, 34062306a36Sopenharmony_ci parent_state); 34162306a36Sopenharmony_ci ret = _genpd_set_performance_state(parent, parent_state, depth + 1); 34262306a36Sopenharmony_ci if (ret) 34362306a36Sopenharmony_ci link->performance_state = link->prev_performance_state; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci genpd_unlock(parent); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (ret) 34862306a36Sopenharmony_ci goto err; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (genpd->set_performance_state) { 35262306a36Sopenharmony_ci ret = genpd->set_performance_state(genpd, state); 35362306a36Sopenharmony_ci if (ret) 35462306a36Sopenharmony_ci goto err; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci genpd->performance_state = state; 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cierr: 36162306a36Sopenharmony_ci /* Encountered an error, lets rollback */ 36262306a36Sopenharmony_ci list_for_each_entry_continue_reverse(link, &genpd->child_links, 36362306a36Sopenharmony_ci child_node) { 36462306a36Sopenharmony_ci parent = link->parent; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci genpd_lock_nested(parent, depth + 1); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci parent_state = link->prev_performance_state; 36962306a36Sopenharmony_ci link->performance_state = parent_state; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci parent_state = _genpd_reeval_performance_state(parent, 37262306a36Sopenharmony_ci parent_state); 37362306a36Sopenharmony_ci if (_genpd_set_performance_state(parent, parent_state, depth + 1)) { 37462306a36Sopenharmony_ci pr_err("%s: Failed to roll back to %d performance state\n", 37562306a36Sopenharmony_ci parent->name, parent_state); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci genpd_unlock(parent); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int genpd_set_performance_state(struct device *dev, unsigned int state) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct generic_pm_domain *genpd = dev_to_genpd(dev); 38762306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); 38862306a36Sopenharmony_ci unsigned int prev_state; 38962306a36Sopenharmony_ci int ret; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci prev_state = gpd_data->performance_state; 39262306a36Sopenharmony_ci if (prev_state == state) 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci gpd_data->performance_state = state; 39662306a36Sopenharmony_ci state = _genpd_reeval_performance_state(genpd, state); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ret = _genpd_set_performance_state(genpd, state, 0); 39962306a36Sopenharmony_ci if (ret) 40062306a36Sopenharmony_ci gpd_data->performance_state = prev_state; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int genpd_drop_performance_state(struct device *dev) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci unsigned int prev_state = dev_gpd_data(dev)->performance_state; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!genpd_set_performance_state(dev, 0)) 41062306a36Sopenharmony_ci return prev_state; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void genpd_restore_performance_state(struct device *dev, 41662306a36Sopenharmony_ci unsigned int state) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci if (state) 41962306a36Sopenharmony_ci genpd_set_performance_state(dev, state); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/** 42362306a36Sopenharmony_ci * dev_pm_genpd_set_performance_state- Set performance state of device's power 42462306a36Sopenharmony_ci * domain. 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * @dev: Device for which the performance-state needs to be set. 42762306a36Sopenharmony_ci * @state: Target performance state of the device. This can be set as 0 when the 42862306a36Sopenharmony_ci * device doesn't have any performance state constraints left (And so 42962306a36Sopenharmony_ci * the device wouldn't participate anymore to find the target 43062306a36Sopenharmony_ci * performance state of the genpd). 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * It is assumed that the users guarantee that the genpd wouldn't be detached 43362306a36Sopenharmony_ci * while this routine is getting called. 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * Returns 0 on success and negative error values on failures. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ciint dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct generic_pm_domain *genpd; 44062306a36Sopenharmony_ci int ret = 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 44362306a36Sopenharmony_ci if (!genpd) 44462306a36Sopenharmony_ci return -ENODEV; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (WARN_ON(!dev->power.subsys_data || 44762306a36Sopenharmony_ci !dev->power.subsys_data->domain_data)) 44862306a36Sopenharmony_ci return -EINVAL; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci genpd_lock(genpd); 45162306a36Sopenharmony_ci if (pm_runtime_suspended(dev)) { 45262306a36Sopenharmony_ci dev_gpd_data(dev)->rpm_pstate = state; 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci ret = genpd_set_performance_state(dev, state); 45562306a36Sopenharmony_ci if (!ret) 45662306a36Sopenharmony_ci dev_gpd_data(dev)->rpm_pstate = 0; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci genpd_unlock(genpd); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/** 46562306a36Sopenharmony_ci * dev_pm_genpd_set_next_wakeup - Notify PM framework of an impending wakeup. 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * @dev: Device to handle 46862306a36Sopenharmony_ci * @next: impending interrupt/wakeup for the device 46962306a36Sopenharmony_ci * 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * Allow devices to inform of the next wakeup. It's assumed that the users 47262306a36Sopenharmony_ci * guarantee that the genpd wouldn't be detached while this routine is getting 47362306a36Sopenharmony_ci * called. Additionally, it's also assumed that @dev isn't runtime suspended 47462306a36Sopenharmony_ci * (RPM_SUSPENDED)." 47562306a36Sopenharmony_ci * Although devices are expected to update the next_wakeup after the end of 47662306a36Sopenharmony_ci * their usecase as well, it is possible the devices themselves may not know 47762306a36Sopenharmony_ci * about that, so stale @next will be ignored when powering off the domain. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_civoid dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct generic_pm_domain *genpd; 48262306a36Sopenharmony_ci struct gpd_timing_data *td; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 48562306a36Sopenharmony_ci if (!genpd) 48662306a36Sopenharmony_ci return; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci td = to_gpd_data(dev->power.subsys_data->domain_data)->td; 48962306a36Sopenharmony_ci if (td) 49062306a36Sopenharmony_ci td->next_wakeup = next; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_set_next_wakeup); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/** 49562306a36Sopenharmony_ci * dev_pm_genpd_get_next_hrtimer - Return the next_hrtimer for the genpd 49662306a36Sopenharmony_ci * @dev: A device that is attached to the genpd. 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * This routine should typically be called for a device, at the point of when a 49962306a36Sopenharmony_ci * GENPD_NOTIFY_PRE_OFF notification has been sent for it. 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * Returns the aggregated value of the genpd's next hrtimer or KTIME_MAX if no 50262306a36Sopenharmony_ci * valid value have been set. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ciktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct generic_pm_domain *genpd; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 50962306a36Sopenharmony_ci if (!genpd) 51062306a36Sopenharmony_ci return KTIME_MAX; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (genpd->gd) 51362306a36Sopenharmony_ci return genpd->gd->next_hrtimer; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return KTIME_MAX; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_get_next_hrtimer); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* 52062306a36Sopenharmony_ci * dev_pm_genpd_synced_poweroff - Next power off should be synchronous 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * @dev: A device that is attached to the genpd. 52362306a36Sopenharmony_ci * 52462306a36Sopenharmony_ci * Allows a consumer of the genpd to notify the provider that the next power off 52562306a36Sopenharmony_ci * should be synchronous. 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * It is assumed that the users guarantee that the genpd wouldn't be detached 52862306a36Sopenharmony_ci * while this routine is getting called. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_civoid dev_pm_genpd_synced_poweroff(struct device *dev) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct generic_pm_domain *genpd; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 53562306a36Sopenharmony_ci if (!genpd) 53662306a36Sopenharmony_ci return; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci genpd_lock(genpd); 53962306a36Sopenharmony_ci genpd->synced_poweroff = true; 54062306a36Sopenharmony_ci genpd_unlock(genpd); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_synced_poweroff); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci unsigned int state_idx = genpd->state_idx; 54762306a36Sopenharmony_ci ktime_t time_start; 54862306a36Sopenharmony_ci s64 elapsed_ns; 54962306a36Sopenharmony_ci int ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Notify consumers that we are about to power on. */ 55262306a36Sopenharmony_ci ret = raw_notifier_call_chain_robust(&genpd->power_notifiers, 55362306a36Sopenharmony_ci GENPD_NOTIFY_PRE_ON, 55462306a36Sopenharmony_ci GENPD_NOTIFY_OFF, NULL); 55562306a36Sopenharmony_ci ret = notifier_to_errno(ret); 55662306a36Sopenharmony_ci if (ret) 55762306a36Sopenharmony_ci return ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (!genpd->power_on) 56062306a36Sopenharmony_ci goto out; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci timed = timed && genpd->gd && !genpd->states[state_idx].fwnode; 56362306a36Sopenharmony_ci if (!timed) { 56462306a36Sopenharmony_ci ret = genpd->power_on(genpd); 56562306a36Sopenharmony_ci if (ret) 56662306a36Sopenharmony_ci goto err; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci goto out; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci time_start = ktime_get(); 57262306a36Sopenharmony_ci ret = genpd->power_on(genpd); 57362306a36Sopenharmony_ci if (ret) 57462306a36Sopenharmony_ci goto err; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 57762306a36Sopenharmony_ci if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 57862306a36Sopenharmony_ci goto out; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 58162306a36Sopenharmony_ci genpd->gd->max_off_time_changed = true; 58262306a36Sopenharmony_ci pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 58362306a36Sopenharmony_ci genpd->name, "on", elapsed_ns); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ciout: 58662306a36Sopenharmony_ci raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL); 58762306a36Sopenharmony_ci genpd->synced_poweroff = false; 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_cierr: 59062306a36Sopenharmony_ci raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF, 59162306a36Sopenharmony_ci NULL); 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci unsigned int state_idx = genpd->state_idx; 59862306a36Sopenharmony_ci ktime_t time_start; 59962306a36Sopenharmony_ci s64 elapsed_ns; 60062306a36Sopenharmony_ci int ret; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Notify consumers that we are about to power off. */ 60362306a36Sopenharmony_ci ret = raw_notifier_call_chain_robust(&genpd->power_notifiers, 60462306a36Sopenharmony_ci GENPD_NOTIFY_PRE_OFF, 60562306a36Sopenharmony_ci GENPD_NOTIFY_ON, NULL); 60662306a36Sopenharmony_ci ret = notifier_to_errno(ret); 60762306a36Sopenharmony_ci if (ret) 60862306a36Sopenharmony_ci return ret; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!genpd->power_off) 61162306a36Sopenharmony_ci goto out; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci timed = timed && genpd->gd && !genpd->states[state_idx].fwnode; 61462306a36Sopenharmony_ci if (!timed) { 61562306a36Sopenharmony_ci ret = genpd->power_off(genpd); 61662306a36Sopenharmony_ci if (ret) 61762306a36Sopenharmony_ci goto busy; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci goto out; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci time_start = ktime_get(); 62362306a36Sopenharmony_ci ret = genpd->power_off(genpd); 62462306a36Sopenharmony_ci if (ret) 62562306a36Sopenharmony_ci goto busy; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 62862306a36Sopenharmony_ci if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 62962306a36Sopenharmony_ci goto out; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 63262306a36Sopenharmony_ci genpd->gd->max_off_time_changed = true; 63362306a36Sopenharmony_ci pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 63462306a36Sopenharmony_ci genpd->name, "off", elapsed_ns); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ciout: 63762306a36Sopenharmony_ci raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF, 63862306a36Sopenharmony_ci NULL); 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_cibusy: 64162306a36Sopenharmony_ci raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL); 64262306a36Sopenharmony_ci return ret; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/** 64662306a36Sopenharmony_ci * genpd_queue_power_off_work - Queue up the execution of genpd_power_off(). 64762306a36Sopenharmony_ci * @genpd: PM domain to power off. 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci * Queue up the execution of genpd_power_off() unless it's already been done 65062306a36Sopenharmony_ci * before. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_cistatic void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci queue_work(pm_wq, &genpd->power_off_work); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/** 65862306a36Sopenharmony_ci * genpd_power_off - Remove power from a given PM domain. 65962306a36Sopenharmony_ci * @genpd: PM domain to power down. 66062306a36Sopenharmony_ci * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the 66162306a36Sopenharmony_ci * RPM status of the releated device is in an intermediate state, not yet turned 66262306a36Sopenharmony_ci * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not 66362306a36Sopenharmony_ci * be RPM_SUSPENDED, while it tries to power off the PM domain. 66462306a36Sopenharmony_ci * @depth: nesting count for lockdep. 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * If all of the @genpd's devices have been suspended and all of its subdomains 66762306a36Sopenharmony_ci * have been powered down, remove power from @genpd. 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_cistatic int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, 67062306a36Sopenharmony_ci unsigned int depth) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct pm_domain_data *pdd; 67362306a36Sopenharmony_ci struct gpd_link *link; 67462306a36Sopenharmony_ci unsigned int not_suspended = 0; 67562306a36Sopenharmony_ci int ret; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* 67862306a36Sopenharmony_ci * Do not try to power off the domain in the following situations: 67962306a36Sopenharmony_ci * (1) The domain is already in the "power off" state. 68062306a36Sopenharmony_ci * (2) System suspend is in progress. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci if (!genpd_status_on(genpd) || genpd->prepared_count > 0) 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* 68662306a36Sopenharmony_ci * Abort power off for the PM domain in the following situations: 68762306a36Sopenharmony_ci * (1) The domain is configured as always on. 68862306a36Sopenharmony_ci * (2) When the domain has a subdomain being powered on. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci if (genpd_is_always_on(genpd) || 69162306a36Sopenharmony_ci genpd_is_rpm_always_on(genpd) || 69262306a36Sopenharmony_ci atomic_read(&genpd->sd_count) > 0) 69362306a36Sopenharmony_ci return -EBUSY; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* 69662306a36Sopenharmony_ci * The children must be in their deepest (powered-off) states to allow 69762306a36Sopenharmony_ci * the parent to be powered off. Note that, there's no need for 69862306a36Sopenharmony_ci * additional locking, as powering on a child, requires the parent's 69962306a36Sopenharmony_ci * lock to be acquired first. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci list_for_each_entry(link, &genpd->parent_links, parent_node) { 70262306a36Sopenharmony_ci struct generic_pm_domain *child = link->child; 70362306a36Sopenharmony_ci if (child->state_idx < child->state_count - 1) 70462306a36Sopenharmony_ci return -EBUSY; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci list_for_each_entry(pdd, &genpd->dev_list, list_node) { 70862306a36Sopenharmony_ci /* 70962306a36Sopenharmony_ci * Do not allow PM domain to be powered off, when an IRQ safe 71062306a36Sopenharmony_ci * device is part of a non-IRQ safe domain. 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci if (!pm_runtime_suspended(pdd->dev) || 71362306a36Sopenharmony_ci irq_safe_dev_in_sleep_domain(pdd->dev, genpd)) 71462306a36Sopenharmony_ci not_suspended++; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) 71862306a36Sopenharmony_ci return -EBUSY; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (genpd->gov && genpd->gov->power_down_ok) { 72162306a36Sopenharmony_ci if (!genpd->gov->power_down_ok(&genpd->domain)) 72262306a36Sopenharmony_ci return -EAGAIN; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* Default to shallowest state. */ 72662306a36Sopenharmony_ci if (!genpd->gov) 72762306a36Sopenharmony_ci genpd->state_idx = 0; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Don't power off, if a child domain is waiting to power on. */ 73062306a36Sopenharmony_ci if (atomic_read(&genpd->sd_count) > 0) 73162306a36Sopenharmony_ci return -EBUSY; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = _genpd_power_off(genpd, true); 73462306a36Sopenharmony_ci if (ret) { 73562306a36Sopenharmony_ci genpd->states[genpd->state_idx].rejected++; 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci genpd->status = GENPD_STATE_OFF; 74062306a36Sopenharmony_ci genpd_update_accounting(genpd); 74162306a36Sopenharmony_ci genpd->states[genpd->state_idx].usage++; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci list_for_each_entry(link, &genpd->child_links, child_node) { 74462306a36Sopenharmony_ci genpd_sd_counter_dec(link->parent); 74562306a36Sopenharmony_ci genpd_lock_nested(link->parent, depth + 1); 74662306a36Sopenharmony_ci genpd_power_off(link->parent, false, depth + 1); 74762306a36Sopenharmony_ci genpd_unlock(link->parent); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return 0; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/** 75462306a36Sopenharmony_ci * genpd_power_on - Restore power to a given PM domain and its parents. 75562306a36Sopenharmony_ci * @genpd: PM domain to power up. 75662306a36Sopenharmony_ci * @depth: nesting count for lockdep. 75762306a36Sopenharmony_ci * 75862306a36Sopenharmony_ci * Restore power to @genpd and all of its parents so that it is possible to 75962306a36Sopenharmony_ci * resume a device belonging to it. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_cistatic int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct gpd_link *link; 76462306a36Sopenharmony_ci int ret = 0; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (genpd_status_on(genpd)) 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * The list is guaranteed not to change while the loop below is being 77162306a36Sopenharmony_ci * executed, unless one of the parents' .power_on() callbacks fiddles 77262306a36Sopenharmony_ci * with it. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ci list_for_each_entry(link, &genpd->child_links, child_node) { 77562306a36Sopenharmony_ci struct generic_pm_domain *parent = link->parent; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci genpd_sd_counter_inc(parent); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci genpd_lock_nested(parent, depth + 1); 78062306a36Sopenharmony_ci ret = genpd_power_on(parent, depth + 1); 78162306a36Sopenharmony_ci genpd_unlock(parent); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (ret) { 78462306a36Sopenharmony_ci genpd_sd_counter_dec(parent); 78562306a36Sopenharmony_ci goto err; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = _genpd_power_on(genpd, true); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci goto err; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci genpd->status = GENPD_STATE_ON; 79462306a36Sopenharmony_ci genpd_update_accounting(genpd); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci err: 79962306a36Sopenharmony_ci list_for_each_entry_continue_reverse(link, 80062306a36Sopenharmony_ci &genpd->child_links, 80162306a36Sopenharmony_ci child_node) { 80262306a36Sopenharmony_ci genpd_sd_counter_dec(link->parent); 80362306a36Sopenharmony_ci genpd_lock_nested(link->parent, depth + 1); 80462306a36Sopenharmony_ci genpd_power_off(link->parent, false, depth + 1); 80562306a36Sopenharmony_ci genpd_unlock(link->parent); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return ret; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int genpd_dev_pm_start(struct device *dev) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct generic_pm_domain *genpd = dev_to_genpd(dev); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return genpd_start_dev(genpd, dev); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 81962306a36Sopenharmony_ci unsigned long val, void *ptr) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 82262306a36Sopenharmony_ci struct device *dev; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 82562306a36Sopenharmony_ci dev = gpd_data->base.dev; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci for (;;) { 82862306a36Sopenharmony_ci struct generic_pm_domain *genpd = ERR_PTR(-ENODATA); 82962306a36Sopenharmony_ci struct pm_domain_data *pdd; 83062306a36Sopenharmony_ci struct gpd_timing_data *td; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci spin_lock_irq(&dev->power.lock); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci pdd = dev->power.subsys_data ? 83562306a36Sopenharmony_ci dev->power.subsys_data->domain_data : NULL; 83662306a36Sopenharmony_ci if (pdd) { 83762306a36Sopenharmony_ci td = to_gpd_data(pdd)->td; 83862306a36Sopenharmony_ci if (td) { 83962306a36Sopenharmony_ci td->constraint_changed = true; 84062306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (!IS_ERR(genpd)) { 84762306a36Sopenharmony_ci genpd_lock(genpd); 84862306a36Sopenharmony_ci genpd->gd->max_off_time_changed = true; 84962306a36Sopenharmony_ci genpd_unlock(genpd); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci dev = dev->parent; 85362306a36Sopenharmony_ci if (!dev || dev->power.ignore_children) 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return NOTIFY_DONE; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci/** 86162306a36Sopenharmony_ci * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 86262306a36Sopenharmony_ci * @work: Work structure used for scheduling the execution of this function. 86362306a36Sopenharmony_ci */ 86462306a36Sopenharmony_cistatic void genpd_power_off_work_fn(struct work_struct *work) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct generic_pm_domain *genpd; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci genpd = container_of(work, struct generic_pm_domain, power_off_work); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci genpd_lock(genpd); 87162306a36Sopenharmony_ci genpd_power_off(genpd, false, 0); 87262306a36Sopenharmony_ci genpd_unlock(genpd); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci/** 87662306a36Sopenharmony_ci * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 87762306a36Sopenharmony_ci * @dev: Device to handle. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_cistatic int __genpd_runtime_suspend(struct device *dev) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci int (*cb)(struct device *__dev); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (dev->type && dev->type->pm) 88462306a36Sopenharmony_ci cb = dev->type->pm->runtime_suspend; 88562306a36Sopenharmony_ci else if (dev->class && dev->class->pm) 88662306a36Sopenharmony_ci cb = dev->class->pm->runtime_suspend; 88762306a36Sopenharmony_ci else if (dev->bus && dev->bus->pm) 88862306a36Sopenharmony_ci cb = dev->bus->pm->runtime_suspend; 88962306a36Sopenharmony_ci else 89062306a36Sopenharmony_ci cb = NULL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (!cb && dev->driver && dev->driver->pm) 89362306a36Sopenharmony_ci cb = dev->driver->pm->runtime_suspend; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return cb ? cb(dev) : 0; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/** 89962306a36Sopenharmony_ci * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 90062306a36Sopenharmony_ci * @dev: Device to handle. 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_cistatic int __genpd_runtime_resume(struct device *dev) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci int (*cb)(struct device *__dev); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (dev->type && dev->type->pm) 90762306a36Sopenharmony_ci cb = dev->type->pm->runtime_resume; 90862306a36Sopenharmony_ci else if (dev->class && dev->class->pm) 90962306a36Sopenharmony_ci cb = dev->class->pm->runtime_resume; 91062306a36Sopenharmony_ci else if (dev->bus && dev->bus->pm) 91162306a36Sopenharmony_ci cb = dev->bus->pm->runtime_resume; 91262306a36Sopenharmony_ci else 91362306a36Sopenharmony_ci cb = NULL; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!cb && dev->driver && dev->driver->pm) 91662306a36Sopenharmony_ci cb = dev->driver->pm->runtime_resume; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return cb ? cb(dev) : 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/** 92262306a36Sopenharmony_ci * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 92362306a36Sopenharmony_ci * @dev: Device to suspend. 92462306a36Sopenharmony_ci * 92562306a36Sopenharmony_ci * Carry out a runtime suspend of a device under the assumption that its 92662306a36Sopenharmony_ci * pm_domain field points to the domain member of an object of type 92762306a36Sopenharmony_ci * struct generic_pm_domain representing a PM domain consisting of I/O devices. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_cistatic int genpd_runtime_suspend(struct device *dev) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct generic_pm_domain *genpd; 93262306a36Sopenharmony_ci bool (*suspend_ok)(struct device *__dev); 93362306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); 93462306a36Sopenharmony_ci struct gpd_timing_data *td = gpd_data->td; 93562306a36Sopenharmony_ci bool runtime_pm = pm_runtime_enabled(dev); 93662306a36Sopenharmony_ci ktime_t time_start = 0; 93762306a36Sopenharmony_ci s64 elapsed_ns; 93862306a36Sopenharmony_ci int ret; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 94362306a36Sopenharmony_ci if (IS_ERR(genpd)) 94462306a36Sopenharmony_ci return -EINVAL; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* 94762306a36Sopenharmony_ci * A runtime PM centric subsystem/driver may re-use the runtime PM 94862306a36Sopenharmony_ci * callbacks for other purposes than runtime PM. In those scenarios 94962306a36Sopenharmony_ci * runtime PM is disabled. Under these circumstances, we shall skip 95062306a36Sopenharmony_ci * validating/measuring the PM QoS latency. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ci suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 95362306a36Sopenharmony_ci if (runtime_pm && suspend_ok && !suspend_ok(dev)) 95462306a36Sopenharmony_ci return -EBUSY; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Measure suspend latency. */ 95762306a36Sopenharmony_ci if (td && runtime_pm) 95862306a36Sopenharmony_ci time_start = ktime_get(); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ret = __genpd_runtime_suspend(dev); 96162306a36Sopenharmony_ci if (ret) 96262306a36Sopenharmony_ci return ret; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci ret = genpd_stop_dev(genpd, dev); 96562306a36Sopenharmony_ci if (ret) { 96662306a36Sopenharmony_ci __genpd_runtime_resume(dev); 96762306a36Sopenharmony_ci return ret; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* Update suspend latency value if the measured time exceeds it. */ 97162306a36Sopenharmony_ci if (td && runtime_pm) { 97262306a36Sopenharmony_ci elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 97362306a36Sopenharmony_ci if (elapsed_ns > td->suspend_latency_ns) { 97462306a36Sopenharmony_ci td->suspend_latency_ns = elapsed_ns; 97562306a36Sopenharmony_ci dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 97662306a36Sopenharmony_ci elapsed_ns); 97762306a36Sopenharmony_ci genpd->gd->max_off_time_changed = true; 97862306a36Sopenharmony_ci td->constraint_changed = true; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* 98362306a36Sopenharmony_ci * If power.irq_safe is set, this routine may be run with 98462306a36Sopenharmony_ci * IRQs disabled, so suspend only if the PM domain also is irq_safe. 98562306a36Sopenharmony_ci */ 98662306a36Sopenharmony_ci if (irq_safe_dev_in_sleep_domain(dev, genpd)) 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci genpd_lock(genpd); 99062306a36Sopenharmony_ci genpd_power_off(genpd, true, 0); 99162306a36Sopenharmony_ci gpd_data->rpm_pstate = genpd_drop_performance_state(dev); 99262306a36Sopenharmony_ci genpd_unlock(genpd); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci return 0; 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci/** 99862306a36Sopenharmony_ci * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 99962306a36Sopenharmony_ci * @dev: Device to resume. 100062306a36Sopenharmony_ci * 100162306a36Sopenharmony_ci * Carry out a runtime resume of a device under the assumption that its 100262306a36Sopenharmony_ci * pm_domain field points to the domain member of an object of type 100362306a36Sopenharmony_ci * struct generic_pm_domain representing a PM domain consisting of I/O devices. 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_cistatic int genpd_runtime_resume(struct device *dev) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct generic_pm_domain *genpd; 100862306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); 100962306a36Sopenharmony_ci struct gpd_timing_data *td = gpd_data->td; 101062306a36Sopenharmony_ci bool timed = td && pm_runtime_enabled(dev); 101162306a36Sopenharmony_ci ktime_t time_start = 0; 101262306a36Sopenharmony_ci s64 elapsed_ns; 101362306a36Sopenharmony_ci int ret; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 101862306a36Sopenharmony_ci if (IS_ERR(genpd)) 101962306a36Sopenharmony_ci return -EINVAL; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* 102262306a36Sopenharmony_ci * As we don't power off a non IRQ safe domain, which holds 102362306a36Sopenharmony_ci * an IRQ safe device, we don't need to restore power to it. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci if (irq_safe_dev_in_sleep_domain(dev, genpd)) 102662306a36Sopenharmony_ci goto out; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci genpd_lock(genpd); 102962306a36Sopenharmony_ci genpd_restore_performance_state(dev, gpd_data->rpm_pstate); 103062306a36Sopenharmony_ci ret = genpd_power_on(genpd, 0); 103162306a36Sopenharmony_ci genpd_unlock(genpd); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (ret) 103462306a36Sopenharmony_ci return ret; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci out: 103762306a36Sopenharmony_ci /* Measure resume latency. */ 103862306a36Sopenharmony_ci if (timed) 103962306a36Sopenharmony_ci time_start = ktime_get(); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci ret = genpd_start_dev(genpd, dev); 104262306a36Sopenharmony_ci if (ret) 104362306a36Sopenharmony_ci goto err_poweroff; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci ret = __genpd_runtime_resume(dev); 104662306a36Sopenharmony_ci if (ret) 104762306a36Sopenharmony_ci goto err_stop; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Update resume latency value if the measured time exceeds it. */ 105062306a36Sopenharmony_ci if (timed) { 105162306a36Sopenharmony_ci elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 105262306a36Sopenharmony_ci if (elapsed_ns > td->resume_latency_ns) { 105362306a36Sopenharmony_ci td->resume_latency_ns = elapsed_ns; 105462306a36Sopenharmony_ci dev_dbg(dev, "resume latency exceeded, %lld ns\n", 105562306a36Sopenharmony_ci elapsed_ns); 105662306a36Sopenharmony_ci genpd->gd->max_off_time_changed = true; 105762306a36Sopenharmony_ci td->constraint_changed = true; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci return 0; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cierr_stop: 106462306a36Sopenharmony_ci genpd_stop_dev(genpd, dev); 106562306a36Sopenharmony_cierr_poweroff: 106662306a36Sopenharmony_ci if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) { 106762306a36Sopenharmony_ci genpd_lock(genpd); 106862306a36Sopenharmony_ci genpd_power_off(genpd, true, 0); 106962306a36Sopenharmony_ci gpd_data->rpm_pstate = genpd_drop_performance_state(dev); 107062306a36Sopenharmony_ci genpd_unlock(genpd); 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci return ret; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic bool pd_ignore_unused; 107762306a36Sopenharmony_cistatic int __init pd_ignore_unused_setup(char *__unused) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci pd_ignore_unused = true; 108062306a36Sopenharmony_ci return 1; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci__setup("pd_ignore_unused", pd_ignore_unused_setup); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci/** 108562306a36Sopenharmony_ci * genpd_power_off_unused - Power off all PM domains with no devices in use. 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_cistatic int __init genpd_power_off_unused(void) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct generic_pm_domain *genpd; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (pd_ignore_unused) { 109262306a36Sopenharmony_ci pr_warn("genpd: Not disabling unused power domains\n"); 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci list_for_each_entry(genpd, &gpd_list, gpd_list_node) 109962306a36Sopenharmony_ci genpd_queue_power_off_work(genpd); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci return 0; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_cilate_initcall_sync(genpd_power_off_unused); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci/** 111062306a36Sopenharmony_ci * genpd_sync_power_off - Synchronously power off a PM domain and its parents. 111162306a36Sopenharmony_ci * @genpd: PM domain to power off, if possible. 111262306a36Sopenharmony_ci * @use_lock: use the lock. 111362306a36Sopenharmony_ci * @depth: nesting count for lockdep. 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci * Check if the given PM domain can be powered off (during system suspend or 111662306a36Sopenharmony_ci * hibernation) and do that if so. Also, in that case propagate to its parents. 111762306a36Sopenharmony_ci * 111862306a36Sopenharmony_ci * This function is only called in "noirq" and "syscore" stages of system power 111962306a36Sopenharmony_ci * transitions. The "noirq" callbacks may be executed asynchronously, thus in 112062306a36Sopenharmony_ci * these cases the lock must be held. 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_cistatic void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, 112362306a36Sopenharmony_ci unsigned int depth) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct gpd_link *link; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (!genpd_status_on(genpd) || genpd_is_always_on(genpd)) 112862306a36Sopenharmony_ci return; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (genpd->suspended_count != genpd->device_count 113162306a36Sopenharmony_ci || atomic_read(&genpd->sd_count) > 0) 113262306a36Sopenharmony_ci return; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Check that the children are in their deepest (powered-off) state. */ 113562306a36Sopenharmony_ci list_for_each_entry(link, &genpd->parent_links, parent_node) { 113662306a36Sopenharmony_ci struct generic_pm_domain *child = link->child; 113762306a36Sopenharmony_ci if (child->state_idx < child->state_count - 1) 113862306a36Sopenharmony_ci return; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Choose the deepest state when suspending */ 114262306a36Sopenharmony_ci genpd->state_idx = genpd->state_count - 1; 114362306a36Sopenharmony_ci if (_genpd_power_off(genpd, false)) 114462306a36Sopenharmony_ci return; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci genpd->status = GENPD_STATE_OFF; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci list_for_each_entry(link, &genpd->child_links, child_node) { 114962306a36Sopenharmony_ci genpd_sd_counter_dec(link->parent); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci if (use_lock) 115262306a36Sopenharmony_ci genpd_lock_nested(link->parent, depth + 1); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci genpd_sync_power_off(link->parent, use_lock, depth + 1); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (use_lock) 115762306a36Sopenharmony_ci genpd_unlock(link->parent); 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci/** 116262306a36Sopenharmony_ci * genpd_sync_power_on - Synchronously power on a PM domain and its parents. 116362306a36Sopenharmony_ci * @genpd: PM domain to power on. 116462306a36Sopenharmony_ci * @use_lock: use the lock. 116562306a36Sopenharmony_ci * @depth: nesting count for lockdep. 116662306a36Sopenharmony_ci * 116762306a36Sopenharmony_ci * This function is only called in "noirq" and "syscore" stages of system power 116862306a36Sopenharmony_ci * transitions. The "noirq" callbacks may be executed asynchronously, thus in 116962306a36Sopenharmony_ci * these cases the lock must be held. 117062306a36Sopenharmony_ci */ 117162306a36Sopenharmony_cistatic void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, 117262306a36Sopenharmony_ci unsigned int depth) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct gpd_link *link; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (genpd_status_on(genpd)) 117762306a36Sopenharmony_ci return; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci list_for_each_entry(link, &genpd->child_links, child_node) { 118062306a36Sopenharmony_ci genpd_sd_counter_inc(link->parent); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (use_lock) 118362306a36Sopenharmony_ci genpd_lock_nested(link->parent, depth + 1); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci genpd_sync_power_on(link->parent, use_lock, depth + 1); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (use_lock) 118862306a36Sopenharmony_ci genpd_unlock(link->parent); 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci _genpd_power_on(genpd, false); 119262306a36Sopenharmony_ci genpd->status = GENPD_STATE_ON; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci/** 119662306a36Sopenharmony_ci * genpd_prepare - Start power transition of a device in a PM domain. 119762306a36Sopenharmony_ci * @dev: Device to start the transition of. 119862306a36Sopenharmony_ci * 119962306a36Sopenharmony_ci * Start a power transition of a device (during a system-wide power transition) 120062306a36Sopenharmony_ci * under the assumption that its pm_domain field points to the domain member of 120162306a36Sopenharmony_ci * an object of type struct generic_pm_domain representing a PM domain 120262306a36Sopenharmony_ci * consisting of I/O devices. 120362306a36Sopenharmony_ci */ 120462306a36Sopenharmony_cistatic int genpd_prepare(struct device *dev) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct generic_pm_domain *genpd; 120762306a36Sopenharmony_ci int ret; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 121262306a36Sopenharmony_ci if (IS_ERR(genpd)) 121362306a36Sopenharmony_ci return -EINVAL; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci genpd_lock(genpd); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (genpd->prepared_count++ == 0) 121862306a36Sopenharmony_ci genpd->suspended_count = 0; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci genpd_unlock(genpd); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci ret = pm_generic_prepare(dev); 122362306a36Sopenharmony_ci if (ret < 0) { 122462306a36Sopenharmony_ci genpd_lock(genpd); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci genpd->prepared_count--; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci genpd_unlock(genpd); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* Never return 1, as genpd don't cope with the direct_complete path. */ 123262306a36Sopenharmony_ci return ret >= 0 ? 0 : ret; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci/** 123662306a36Sopenharmony_ci * genpd_finish_suspend - Completion of suspend or hibernation of device in an 123762306a36Sopenharmony_ci * I/O pm domain. 123862306a36Sopenharmony_ci * @dev: Device to suspend. 123962306a36Sopenharmony_ci * @suspend_noirq: Generic suspend_noirq callback. 124062306a36Sopenharmony_ci * @resume_noirq: Generic resume_noirq callback. 124162306a36Sopenharmony_ci * 124262306a36Sopenharmony_ci * Stop the device and remove power from the domain if all devices in it have 124362306a36Sopenharmony_ci * been stopped. 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_cistatic int genpd_finish_suspend(struct device *dev, 124662306a36Sopenharmony_ci int (*suspend_noirq)(struct device *dev), 124762306a36Sopenharmony_ci int (*resume_noirq)(struct device *dev)) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci struct generic_pm_domain *genpd; 125062306a36Sopenharmony_ci int ret = 0; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 125362306a36Sopenharmony_ci if (IS_ERR(genpd)) 125462306a36Sopenharmony_ci return -EINVAL; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci ret = suspend_noirq(dev); 125762306a36Sopenharmony_ci if (ret) 125862306a36Sopenharmony_ci return ret; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) 126162306a36Sopenharmony_ci return 0; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (genpd->dev_ops.stop && genpd->dev_ops.start && 126462306a36Sopenharmony_ci !pm_runtime_status_suspended(dev)) { 126562306a36Sopenharmony_ci ret = genpd_stop_dev(genpd, dev); 126662306a36Sopenharmony_ci if (ret) { 126762306a36Sopenharmony_ci resume_noirq(dev); 126862306a36Sopenharmony_ci return ret; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci genpd_lock(genpd); 127362306a36Sopenharmony_ci genpd->suspended_count++; 127462306a36Sopenharmony_ci genpd_sync_power_off(genpd, true, 0); 127562306a36Sopenharmony_ci genpd_unlock(genpd); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci return 0; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci/** 128162306a36Sopenharmony_ci * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 128262306a36Sopenharmony_ci * @dev: Device to suspend. 128362306a36Sopenharmony_ci * 128462306a36Sopenharmony_ci * Stop the device and remove power from the domain if all devices in it have 128562306a36Sopenharmony_ci * been stopped. 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_cistatic int genpd_suspend_noirq(struct device *dev) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return genpd_finish_suspend(dev, 129262306a36Sopenharmony_ci pm_generic_suspend_noirq, 129362306a36Sopenharmony_ci pm_generic_resume_noirq); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci/** 129762306a36Sopenharmony_ci * genpd_finish_resume - Completion of resume of device in an I/O PM domain. 129862306a36Sopenharmony_ci * @dev: Device to resume. 129962306a36Sopenharmony_ci * @resume_noirq: Generic resume_noirq callback. 130062306a36Sopenharmony_ci * 130162306a36Sopenharmony_ci * Restore power to the device's PM domain, if necessary, and start the device. 130262306a36Sopenharmony_ci */ 130362306a36Sopenharmony_cistatic int genpd_finish_resume(struct device *dev, 130462306a36Sopenharmony_ci int (*resume_noirq)(struct device *dev)) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci struct generic_pm_domain *genpd; 130762306a36Sopenharmony_ci int ret; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 131262306a36Sopenharmony_ci if (IS_ERR(genpd)) 131362306a36Sopenharmony_ci return -EINVAL; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) 131662306a36Sopenharmony_ci return resume_noirq(dev); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci genpd_lock(genpd); 131962306a36Sopenharmony_ci genpd_sync_power_on(genpd, true, 0); 132062306a36Sopenharmony_ci genpd->suspended_count--; 132162306a36Sopenharmony_ci genpd_unlock(genpd); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (genpd->dev_ops.stop && genpd->dev_ops.start && 132462306a36Sopenharmony_ci !pm_runtime_status_suspended(dev)) { 132562306a36Sopenharmony_ci ret = genpd_start_dev(genpd, dev); 132662306a36Sopenharmony_ci if (ret) 132762306a36Sopenharmony_ci return ret; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci return pm_generic_resume_noirq(dev); 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci/** 133462306a36Sopenharmony_ci * genpd_resume_noirq - Start of resume of device in an I/O PM domain. 133562306a36Sopenharmony_ci * @dev: Device to resume. 133662306a36Sopenharmony_ci * 133762306a36Sopenharmony_ci * Restore power to the device's PM domain, if necessary, and start the device. 133862306a36Sopenharmony_ci */ 133962306a36Sopenharmony_cistatic int genpd_resume_noirq(struct device *dev) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci return genpd_finish_resume(dev, pm_generic_resume_noirq); 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci/** 134762306a36Sopenharmony_ci * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 134862306a36Sopenharmony_ci * @dev: Device to freeze. 134962306a36Sopenharmony_ci * 135062306a36Sopenharmony_ci * Carry out a late freeze of a device under the assumption that its 135162306a36Sopenharmony_ci * pm_domain field points to the domain member of an object of type 135262306a36Sopenharmony_ci * struct generic_pm_domain representing a power domain consisting of I/O 135362306a36Sopenharmony_ci * devices. 135462306a36Sopenharmony_ci */ 135562306a36Sopenharmony_cistatic int genpd_freeze_noirq(struct device *dev) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci return genpd_finish_suspend(dev, 136062306a36Sopenharmony_ci pm_generic_freeze_noirq, 136162306a36Sopenharmony_ci pm_generic_thaw_noirq); 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci/** 136562306a36Sopenharmony_ci * genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 136662306a36Sopenharmony_ci * @dev: Device to thaw. 136762306a36Sopenharmony_ci * 136862306a36Sopenharmony_ci * Start the device, unless power has been removed from the domain already 136962306a36Sopenharmony_ci * before the system transition. 137062306a36Sopenharmony_ci */ 137162306a36Sopenharmony_cistatic int genpd_thaw_noirq(struct device *dev) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci return genpd_finish_resume(dev, pm_generic_thaw_noirq); 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci/** 137962306a36Sopenharmony_ci * genpd_poweroff_noirq - Completion of hibernation of device in an 138062306a36Sopenharmony_ci * I/O PM domain. 138162306a36Sopenharmony_ci * @dev: Device to poweroff. 138262306a36Sopenharmony_ci * 138362306a36Sopenharmony_ci * Stop the device and remove power from the domain if all devices in it have 138462306a36Sopenharmony_ci * been stopped. 138562306a36Sopenharmony_ci */ 138662306a36Sopenharmony_cistatic int genpd_poweroff_noirq(struct device *dev) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return genpd_finish_suspend(dev, 139162306a36Sopenharmony_ci pm_generic_poweroff_noirq, 139262306a36Sopenharmony_ci pm_generic_restore_noirq); 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci/** 139662306a36Sopenharmony_ci * genpd_restore_noirq - Start of restore of device in an I/O PM domain. 139762306a36Sopenharmony_ci * @dev: Device to resume. 139862306a36Sopenharmony_ci * 139962306a36Sopenharmony_ci * Make sure the domain will be in the same power state as before the 140062306a36Sopenharmony_ci * hibernation the system is resuming from and start the device if necessary. 140162306a36Sopenharmony_ci */ 140262306a36Sopenharmony_cistatic int genpd_restore_noirq(struct device *dev) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci return genpd_finish_resume(dev, pm_generic_restore_noirq); 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci/** 141062306a36Sopenharmony_ci * genpd_complete - Complete power transition of a device in a power domain. 141162306a36Sopenharmony_ci * @dev: Device to complete the transition of. 141262306a36Sopenharmony_ci * 141362306a36Sopenharmony_ci * Complete a power transition of a device (during a system-wide power 141462306a36Sopenharmony_ci * transition) under the assumption that its pm_domain field points to the 141562306a36Sopenharmony_ci * domain member of an object of type struct generic_pm_domain representing 141662306a36Sopenharmony_ci * a power domain consisting of I/O devices. 141762306a36Sopenharmony_ci */ 141862306a36Sopenharmony_cistatic void genpd_complete(struct device *dev) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct generic_pm_domain *genpd; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci genpd = dev_to_genpd(dev); 142562306a36Sopenharmony_ci if (IS_ERR(genpd)) 142662306a36Sopenharmony_ci return; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci pm_generic_complete(dev); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci genpd_lock(genpd); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci genpd->prepared_count--; 143362306a36Sopenharmony_ci if (!genpd->prepared_count) 143462306a36Sopenharmony_ci genpd_queue_power_off_work(genpd); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci genpd_unlock(genpd); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic void genpd_switch_state(struct device *dev, bool suspend) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci struct generic_pm_domain *genpd; 144262306a36Sopenharmony_ci bool use_lock; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 144562306a36Sopenharmony_ci if (!genpd) 144662306a36Sopenharmony_ci return; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci use_lock = genpd_is_irq_safe(genpd); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (use_lock) 145162306a36Sopenharmony_ci genpd_lock(genpd); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (suspend) { 145462306a36Sopenharmony_ci genpd->suspended_count++; 145562306a36Sopenharmony_ci genpd_sync_power_off(genpd, use_lock, 0); 145662306a36Sopenharmony_ci } else { 145762306a36Sopenharmony_ci genpd_sync_power_on(genpd, use_lock, 0); 145862306a36Sopenharmony_ci genpd->suspended_count--; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (use_lock) 146262306a36Sopenharmony_ci genpd_unlock(genpd); 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci/** 146662306a36Sopenharmony_ci * dev_pm_genpd_suspend - Synchronously try to suspend the genpd for @dev 146762306a36Sopenharmony_ci * @dev: The device that is attached to the genpd, that can be suspended. 146862306a36Sopenharmony_ci * 146962306a36Sopenharmony_ci * This routine should typically be called for a device that needs to be 147062306a36Sopenharmony_ci * suspended during the syscore suspend phase. It may also be called during 147162306a36Sopenharmony_ci * suspend-to-idle to suspend a corresponding CPU device that is attached to a 147262306a36Sopenharmony_ci * genpd. 147362306a36Sopenharmony_ci */ 147462306a36Sopenharmony_civoid dev_pm_genpd_suspend(struct device *dev) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci genpd_switch_state(dev, true); 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_suspend); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci/** 148162306a36Sopenharmony_ci * dev_pm_genpd_resume - Synchronously try to resume the genpd for @dev 148262306a36Sopenharmony_ci * @dev: The device that is attached to the genpd, which needs to be resumed. 148362306a36Sopenharmony_ci * 148462306a36Sopenharmony_ci * This routine should typically be called for a device that needs to be resumed 148562306a36Sopenharmony_ci * during the syscore resume phase. It may also be called during suspend-to-idle 148662306a36Sopenharmony_ci * to resume a corresponding CPU device that is attached to a genpd. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_civoid dev_pm_genpd_resume(struct device *dev) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci genpd_switch_state(dev, false); 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_resume); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci#else /* !CONFIG_PM_SLEEP */ 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci#define genpd_prepare NULL 149762306a36Sopenharmony_ci#define genpd_suspend_noirq NULL 149862306a36Sopenharmony_ci#define genpd_resume_noirq NULL 149962306a36Sopenharmony_ci#define genpd_freeze_noirq NULL 150062306a36Sopenharmony_ci#define genpd_thaw_noirq NULL 150162306a36Sopenharmony_ci#define genpd_poweroff_noirq NULL 150262306a36Sopenharmony_ci#define genpd_restore_noirq NULL 150362306a36Sopenharmony_ci#define genpd_complete NULL 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 150862306a36Sopenharmony_ci bool has_governor) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 151162306a36Sopenharmony_ci struct gpd_timing_data *td; 151262306a36Sopenharmony_ci int ret; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci ret = dev_pm_get_subsys_data(dev); 151562306a36Sopenharmony_ci if (ret) 151662306a36Sopenharmony_ci return ERR_PTR(ret); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 151962306a36Sopenharmony_ci if (!gpd_data) { 152062306a36Sopenharmony_ci ret = -ENOMEM; 152162306a36Sopenharmony_ci goto err_put; 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci gpd_data->base.dev = dev; 152562306a36Sopenharmony_ci gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci /* Allocate data used by a governor. */ 152862306a36Sopenharmony_ci if (has_governor) { 152962306a36Sopenharmony_ci td = kzalloc(sizeof(*td), GFP_KERNEL); 153062306a36Sopenharmony_ci if (!td) { 153162306a36Sopenharmony_ci ret = -ENOMEM; 153262306a36Sopenharmony_ci goto err_free; 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci td->constraint_changed = true; 153662306a36Sopenharmony_ci td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; 153762306a36Sopenharmony_ci td->next_wakeup = KTIME_MAX; 153862306a36Sopenharmony_ci gpd_data->td = td; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci spin_lock_irq(&dev->power.lock); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci if (dev->power.subsys_data->domain_data) 154462306a36Sopenharmony_ci ret = -EINVAL; 154562306a36Sopenharmony_ci else 154662306a36Sopenharmony_ci dev->power.subsys_data->domain_data = &gpd_data->base; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (ret) 155162306a36Sopenharmony_ci goto err_free; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci return gpd_data; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci err_free: 155662306a36Sopenharmony_ci kfree(gpd_data->td); 155762306a36Sopenharmony_ci kfree(gpd_data); 155862306a36Sopenharmony_ci err_put: 155962306a36Sopenharmony_ci dev_pm_put_subsys_data(dev); 156062306a36Sopenharmony_ci return ERR_PTR(ret); 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic void genpd_free_dev_data(struct device *dev, 156462306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci spin_lock_irq(&dev->power.lock); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci dev->power.subsys_data->domain_data = NULL; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci kfree(gpd_data->td); 157362306a36Sopenharmony_ci kfree(gpd_data); 157462306a36Sopenharmony_ci dev_pm_put_subsys_data(dev); 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistatic void genpd_update_cpumask(struct generic_pm_domain *genpd, 157862306a36Sopenharmony_ci int cpu, bool set, unsigned int depth) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci struct gpd_link *link; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (!genpd_is_cpu_domain(genpd)) 158362306a36Sopenharmony_ci return; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci list_for_each_entry(link, &genpd->child_links, child_node) { 158662306a36Sopenharmony_ci struct generic_pm_domain *parent = link->parent; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci genpd_lock_nested(parent, depth + 1); 158962306a36Sopenharmony_ci genpd_update_cpumask(parent, cpu, set, depth + 1); 159062306a36Sopenharmony_ci genpd_unlock(parent); 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (set) 159462306a36Sopenharmony_ci cpumask_set_cpu(cpu, genpd->cpus); 159562306a36Sopenharmony_ci else 159662306a36Sopenharmony_ci cpumask_clear_cpu(cpu, genpd->cpus); 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_cistatic void genpd_set_cpumask(struct generic_pm_domain *genpd, int cpu) 160062306a36Sopenharmony_ci{ 160162306a36Sopenharmony_ci if (cpu >= 0) 160262306a36Sopenharmony_ci genpd_update_cpumask(genpd, cpu, true, 0); 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic void genpd_clear_cpumask(struct generic_pm_domain *genpd, int cpu) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci if (cpu >= 0) 160862306a36Sopenharmony_ci genpd_update_cpumask(genpd, cpu, false, 0); 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_cistatic int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev) 161262306a36Sopenharmony_ci{ 161362306a36Sopenharmony_ci int cpu; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci if (!genpd_is_cpu_domain(genpd)) 161662306a36Sopenharmony_ci return -1; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 161962306a36Sopenharmony_ci if (get_cpu_device(cpu) == dev) 162062306a36Sopenharmony_ci return cpu; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci return -1; 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 162762306a36Sopenharmony_ci struct device *base_dev) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci struct genpd_governor_data *gd = genpd->gd; 163062306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 163162306a36Sopenharmony_ci int ret; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci gpd_data = genpd_alloc_dev_data(dev, gd); 163662306a36Sopenharmony_ci if (IS_ERR(gpd_data)) 163762306a36Sopenharmony_ci return PTR_ERR(gpd_data); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci gpd_data->cpu = genpd_get_cpu(genpd, base_dev); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 164262306a36Sopenharmony_ci if (ret) 164362306a36Sopenharmony_ci goto out; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci genpd_lock(genpd); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci genpd_set_cpumask(genpd, gpd_data->cpu); 164862306a36Sopenharmony_ci dev_pm_domain_set(dev, &genpd->domain); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci genpd->device_count++; 165162306a36Sopenharmony_ci if (gd) 165262306a36Sopenharmony_ci gd->max_off_time_changed = true; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci genpd_unlock(genpd); 165762306a36Sopenharmony_ci out: 165862306a36Sopenharmony_ci if (ret) 165962306a36Sopenharmony_ci genpd_free_dev_data(dev, gpd_data); 166062306a36Sopenharmony_ci else 166162306a36Sopenharmony_ci dev_pm_qos_add_notifier(dev, &gpd_data->nb, 166262306a36Sopenharmony_ci DEV_PM_QOS_RESUME_LATENCY); 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci return ret; 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci/** 166862306a36Sopenharmony_ci * pm_genpd_add_device - Add a device to an I/O PM domain. 166962306a36Sopenharmony_ci * @genpd: PM domain to add the device to. 167062306a36Sopenharmony_ci * @dev: Device to be added. 167162306a36Sopenharmony_ci */ 167262306a36Sopenharmony_ciint pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci int ret; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci if (!genpd || !dev) 167762306a36Sopenharmony_ci return -EINVAL; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 168062306a36Sopenharmony_ci ret = genpd_add_device(genpd, dev, dev); 168162306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci return ret; 168462306a36Sopenharmony_ci} 168562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_add_device); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_cistatic int genpd_remove_device(struct generic_pm_domain *genpd, 168862306a36Sopenharmony_ci struct device *dev) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 169162306a36Sopenharmony_ci struct pm_domain_data *pdd; 169262306a36Sopenharmony_ci int ret = 0; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci pdd = dev->power.subsys_data->domain_data; 169762306a36Sopenharmony_ci gpd_data = to_gpd_data(pdd); 169862306a36Sopenharmony_ci dev_pm_qos_remove_notifier(dev, &gpd_data->nb, 169962306a36Sopenharmony_ci DEV_PM_QOS_RESUME_LATENCY); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci genpd_lock(genpd); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (genpd->prepared_count > 0) { 170462306a36Sopenharmony_ci ret = -EAGAIN; 170562306a36Sopenharmony_ci goto out; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci genpd->device_count--; 170962306a36Sopenharmony_ci if (genpd->gd) 171062306a36Sopenharmony_ci genpd->gd->max_off_time_changed = true; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci genpd_clear_cpumask(genpd, gpd_data->cpu); 171362306a36Sopenharmony_ci dev_pm_domain_set(dev, NULL); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci list_del_init(&pdd->list_node); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci genpd_unlock(genpd); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (genpd->detach_dev) 172062306a36Sopenharmony_ci genpd->detach_dev(genpd, dev); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci genpd_free_dev_data(dev, gpd_data); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci return 0; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci out: 172762306a36Sopenharmony_ci genpd_unlock(genpd); 172862306a36Sopenharmony_ci dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci return ret; 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci/** 173462306a36Sopenharmony_ci * pm_genpd_remove_device - Remove a device from an I/O PM domain. 173562306a36Sopenharmony_ci * @dev: Device to be removed. 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_ciint pm_genpd_remove_device(struct device *dev) 173862306a36Sopenharmony_ci{ 173962306a36Sopenharmony_ci struct generic_pm_domain *genpd = dev_to_genpd_safe(dev); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (!genpd) 174262306a36Sopenharmony_ci return -EINVAL; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci return genpd_remove_device(genpd, dev); 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_remove_device); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci/** 174962306a36Sopenharmony_ci * dev_pm_genpd_add_notifier - Add a genpd power on/off notifier for @dev 175062306a36Sopenharmony_ci * 175162306a36Sopenharmony_ci * @dev: Device that should be associated with the notifier 175262306a36Sopenharmony_ci * @nb: The notifier block to register 175362306a36Sopenharmony_ci * 175462306a36Sopenharmony_ci * Users may call this function to add a genpd power on/off notifier for an 175562306a36Sopenharmony_ci * attached @dev. Only one notifier per device is allowed. The notifier is 175662306a36Sopenharmony_ci * sent when genpd is powering on/off the PM domain. 175762306a36Sopenharmony_ci * 175862306a36Sopenharmony_ci * It is assumed that the user guarantee that the genpd wouldn't be detached 175962306a36Sopenharmony_ci * while this routine is getting called. 176062306a36Sopenharmony_ci * 176162306a36Sopenharmony_ci * Returns 0 on success and negative error values on failures. 176262306a36Sopenharmony_ci */ 176362306a36Sopenharmony_ciint dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci struct generic_pm_domain *genpd; 176662306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 176762306a36Sopenharmony_ci int ret; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 177062306a36Sopenharmony_ci if (!genpd) 177162306a36Sopenharmony_ci return -ENODEV; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (WARN_ON(!dev->power.subsys_data || 177462306a36Sopenharmony_ci !dev->power.subsys_data->domain_data)) 177562306a36Sopenharmony_ci return -EINVAL; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 177862306a36Sopenharmony_ci if (gpd_data->power_nb) 177962306a36Sopenharmony_ci return -EEXIST; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci genpd_lock(genpd); 178262306a36Sopenharmony_ci ret = raw_notifier_chain_register(&genpd->power_notifiers, nb); 178362306a36Sopenharmony_ci genpd_unlock(genpd); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci if (ret) { 178662306a36Sopenharmony_ci dev_warn(dev, "failed to add notifier for PM domain %s\n", 178762306a36Sopenharmony_ci genpd->name); 178862306a36Sopenharmony_ci return ret; 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci gpd_data->power_nb = nb; 179262306a36Sopenharmony_ci return 0; 179362306a36Sopenharmony_ci} 179462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_add_notifier); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci/** 179762306a36Sopenharmony_ci * dev_pm_genpd_remove_notifier - Remove a genpd power on/off notifier for @dev 179862306a36Sopenharmony_ci * 179962306a36Sopenharmony_ci * @dev: Device that is associated with the notifier 180062306a36Sopenharmony_ci * 180162306a36Sopenharmony_ci * Users may call this function to remove a genpd power on/off notifier for an 180262306a36Sopenharmony_ci * attached @dev. 180362306a36Sopenharmony_ci * 180462306a36Sopenharmony_ci * It is assumed that the user guarantee that the genpd wouldn't be detached 180562306a36Sopenharmony_ci * while this routine is getting called. 180662306a36Sopenharmony_ci * 180762306a36Sopenharmony_ci * Returns 0 on success and negative error values on failures. 180862306a36Sopenharmony_ci */ 180962306a36Sopenharmony_ciint dev_pm_genpd_remove_notifier(struct device *dev) 181062306a36Sopenharmony_ci{ 181162306a36Sopenharmony_ci struct generic_pm_domain *genpd; 181262306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 181362306a36Sopenharmony_ci int ret; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci genpd = dev_to_genpd_safe(dev); 181662306a36Sopenharmony_ci if (!genpd) 181762306a36Sopenharmony_ci return -ENODEV; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci if (WARN_ON(!dev->power.subsys_data || 182062306a36Sopenharmony_ci !dev->power.subsys_data->domain_data)) 182162306a36Sopenharmony_ci return -EINVAL; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 182462306a36Sopenharmony_ci if (!gpd_data->power_nb) 182562306a36Sopenharmony_ci return -ENODEV; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci genpd_lock(genpd); 182862306a36Sopenharmony_ci ret = raw_notifier_chain_unregister(&genpd->power_notifiers, 182962306a36Sopenharmony_ci gpd_data->power_nb); 183062306a36Sopenharmony_ci genpd_unlock(genpd); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci if (ret) { 183362306a36Sopenharmony_ci dev_warn(dev, "failed to remove notifier for PM domain %s\n", 183462306a36Sopenharmony_ci genpd->name); 183562306a36Sopenharmony_ci return ret; 183662306a36Sopenharmony_ci } 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci gpd_data->power_nb = NULL; 183962306a36Sopenharmony_ci return 0; 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_genpd_remove_notifier); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_cistatic int genpd_add_subdomain(struct generic_pm_domain *genpd, 184462306a36Sopenharmony_ci struct generic_pm_domain *subdomain) 184562306a36Sopenharmony_ci{ 184662306a36Sopenharmony_ci struct gpd_link *link, *itr; 184762306a36Sopenharmony_ci int ret = 0; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 185062306a36Sopenharmony_ci || genpd == subdomain) 185162306a36Sopenharmony_ci return -EINVAL; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci /* 185462306a36Sopenharmony_ci * If the domain can be powered on/off in an IRQ safe 185562306a36Sopenharmony_ci * context, ensure that the subdomain can also be 185662306a36Sopenharmony_ci * powered on/off in that context. 185762306a36Sopenharmony_ci */ 185862306a36Sopenharmony_ci if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 185962306a36Sopenharmony_ci WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 186062306a36Sopenharmony_ci genpd->name, subdomain->name); 186162306a36Sopenharmony_ci return -EINVAL; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci link = kzalloc(sizeof(*link), GFP_KERNEL); 186562306a36Sopenharmony_ci if (!link) 186662306a36Sopenharmony_ci return -ENOMEM; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci genpd_lock(subdomain); 186962306a36Sopenharmony_ci genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) { 187262306a36Sopenharmony_ci ret = -EINVAL; 187362306a36Sopenharmony_ci goto out; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci list_for_each_entry(itr, &genpd->parent_links, parent_node) { 187762306a36Sopenharmony_ci if (itr->child == subdomain && itr->parent == genpd) { 187862306a36Sopenharmony_ci ret = -EINVAL; 187962306a36Sopenharmony_ci goto out; 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci link->parent = genpd; 188462306a36Sopenharmony_ci list_add_tail(&link->parent_node, &genpd->parent_links); 188562306a36Sopenharmony_ci link->child = subdomain; 188662306a36Sopenharmony_ci list_add_tail(&link->child_node, &subdomain->child_links); 188762306a36Sopenharmony_ci if (genpd_status_on(subdomain)) 188862306a36Sopenharmony_ci genpd_sd_counter_inc(genpd); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci out: 189162306a36Sopenharmony_ci genpd_unlock(genpd); 189262306a36Sopenharmony_ci genpd_unlock(subdomain); 189362306a36Sopenharmony_ci if (ret) 189462306a36Sopenharmony_ci kfree(link); 189562306a36Sopenharmony_ci return ret; 189662306a36Sopenharmony_ci} 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci/** 189962306a36Sopenharmony_ci * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 190062306a36Sopenharmony_ci * @genpd: Leader PM domain to add the subdomain to. 190162306a36Sopenharmony_ci * @subdomain: Subdomain to be added. 190262306a36Sopenharmony_ci */ 190362306a36Sopenharmony_ciint pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 190462306a36Sopenharmony_ci struct generic_pm_domain *subdomain) 190562306a36Sopenharmony_ci{ 190662306a36Sopenharmony_ci int ret; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 190962306a36Sopenharmony_ci ret = genpd_add_subdomain(genpd, subdomain); 191062306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci return ret; 191362306a36Sopenharmony_ci} 191462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci/** 191762306a36Sopenharmony_ci * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 191862306a36Sopenharmony_ci * @genpd: Leader PM domain to remove the subdomain from. 191962306a36Sopenharmony_ci * @subdomain: Subdomain to be removed. 192062306a36Sopenharmony_ci */ 192162306a36Sopenharmony_ciint pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 192262306a36Sopenharmony_ci struct generic_pm_domain *subdomain) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci struct gpd_link *l, *link; 192562306a36Sopenharmony_ci int ret = -EINVAL; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 192862306a36Sopenharmony_ci return -EINVAL; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci genpd_lock(subdomain); 193162306a36Sopenharmony_ci genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci if (!list_empty(&subdomain->parent_links) || subdomain->device_count) { 193462306a36Sopenharmony_ci pr_warn("%s: unable to remove subdomain %s\n", 193562306a36Sopenharmony_ci genpd->name, subdomain->name); 193662306a36Sopenharmony_ci ret = -EBUSY; 193762306a36Sopenharmony_ci goto out; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci list_for_each_entry_safe(link, l, &genpd->parent_links, parent_node) { 194162306a36Sopenharmony_ci if (link->child != subdomain) 194262306a36Sopenharmony_ci continue; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci list_del(&link->parent_node); 194562306a36Sopenharmony_ci list_del(&link->child_node); 194662306a36Sopenharmony_ci kfree(link); 194762306a36Sopenharmony_ci if (genpd_status_on(subdomain)) 194862306a36Sopenharmony_ci genpd_sd_counter_dec(genpd); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci ret = 0; 195162306a36Sopenharmony_ci break; 195262306a36Sopenharmony_ci } 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ciout: 195562306a36Sopenharmony_ci genpd_unlock(genpd); 195662306a36Sopenharmony_ci genpd_unlock(subdomain); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return ret; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_cistatic void genpd_free_default_power_state(struct genpd_power_state *states, 196362306a36Sopenharmony_ci unsigned int state_count) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci kfree(states); 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_cistatic int genpd_set_default_power_state(struct generic_pm_domain *genpd) 196962306a36Sopenharmony_ci{ 197062306a36Sopenharmony_ci struct genpd_power_state *state; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 197362306a36Sopenharmony_ci if (!state) 197462306a36Sopenharmony_ci return -ENOMEM; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci genpd->states = state; 197762306a36Sopenharmony_ci genpd->state_count = 1; 197862306a36Sopenharmony_ci genpd->free_states = genpd_free_default_power_state; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci return 0; 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_cistatic int genpd_alloc_data(struct generic_pm_domain *genpd) 198462306a36Sopenharmony_ci{ 198562306a36Sopenharmony_ci struct genpd_governor_data *gd = NULL; 198662306a36Sopenharmony_ci int ret; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (genpd_is_cpu_domain(genpd) && 198962306a36Sopenharmony_ci !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL)) 199062306a36Sopenharmony_ci return -ENOMEM; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci if (genpd->gov) { 199362306a36Sopenharmony_ci gd = kzalloc(sizeof(*gd), GFP_KERNEL); 199462306a36Sopenharmony_ci if (!gd) { 199562306a36Sopenharmony_ci ret = -ENOMEM; 199662306a36Sopenharmony_ci goto free; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci gd->max_off_time_ns = -1; 200062306a36Sopenharmony_ci gd->max_off_time_changed = true; 200162306a36Sopenharmony_ci gd->next_wakeup = KTIME_MAX; 200262306a36Sopenharmony_ci gd->next_hrtimer = KTIME_MAX; 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci /* Use only one "off" state if there were no states declared */ 200662306a36Sopenharmony_ci if (genpd->state_count == 0) { 200762306a36Sopenharmony_ci ret = genpd_set_default_power_state(genpd); 200862306a36Sopenharmony_ci if (ret) 200962306a36Sopenharmony_ci goto free; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci genpd->gd = gd; 201362306a36Sopenharmony_ci return 0; 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_cifree: 201662306a36Sopenharmony_ci if (genpd_is_cpu_domain(genpd)) 201762306a36Sopenharmony_ci free_cpumask_var(genpd->cpus); 201862306a36Sopenharmony_ci kfree(gd); 201962306a36Sopenharmony_ci return ret; 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_cistatic void genpd_free_data(struct generic_pm_domain *genpd) 202362306a36Sopenharmony_ci{ 202462306a36Sopenharmony_ci if (genpd_is_cpu_domain(genpd)) 202562306a36Sopenharmony_ci free_cpumask_var(genpd->cpus); 202662306a36Sopenharmony_ci if (genpd->free_states) 202762306a36Sopenharmony_ci genpd->free_states(genpd->states, genpd->state_count); 202862306a36Sopenharmony_ci kfree(genpd->gd); 202962306a36Sopenharmony_ci} 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_cistatic void genpd_lock_init(struct generic_pm_domain *genpd) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 203462306a36Sopenharmony_ci spin_lock_init(&genpd->slock); 203562306a36Sopenharmony_ci genpd->lock_ops = &genpd_spin_ops; 203662306a36Sopenharmony_ci } else { 203762306a36Sopenharmony_ci mutex_init(&genpd->mlock); 203862306a36Sopenharmony_ci genpd->lock_ops = &genpd_mtx_ops; 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci/** 204362306a36Sopenharmony_ci * pm_genpd_init - Initialize a generic I/O PM domain object. 204462306a36Sopenharmony_ci * @genpd: PM domain object to initialize. 204562306a36Sopenharmony_ci * @gov: PM domain governor to associate with the domain (may be NULL). 204662306a36Sopenharmony_ci * @is_off: Initial value of the domain's power_is_off field. 204762306a36Sopenharmony_ci * 204862306a36Sopenharmony_ci * Returns 0 on successful initialization, else a negative error code. 204962306a36Sopenharmony_ci */ 205062306a36Sopenharmony_ciint pm_genpd_init(struct generic_pm_domain *genpd, 205162306a36Sopenharmony_ci struct dev_power_governor *gov, bool is_off) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci int ret; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(genpd)) 205662306a36Sopenharmony_ci return -EINVAL; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci INIT_LIST_HEAD(&genpd->parent_links); 205962306a36Sopenharmony_ci INIT_LIST_HEAD(&genpd->child_links); 206062306a36Sopenharmony_ci INIT_LIST_HEAD(&genpd->dev_list); 206162306a36Sopenharmony_ci RAW_INIT_NOTIFIER_HEAD(&genpd->power_notifiers); 206262306a36Sopenharmony_ci genpd_lock_init(genpd); 206362306a36Sopenharmony_ci genpd->gov = gov; 206462306a36Sopenharmony_ci INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 206562306a36Sopenharmony_ci atomic_set(&genpd->sd_count, 0); 206662306a36Sopenharmony_ci genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; 206762306a36Sopenharmony_ci genpd->device_count = 0; 206862306a36Sopenharmony_ci genpd->provider = NULL; 206962306a36Sopenharmony_ci genpd->has_provider = false; 207062306a36Sopenharmony_ci genpd->accounting_time = ktime_get_mono_fast_ns(); 207162306a36Sopenharmony_ci genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 207262306a36Sopenharmony_ci genpd->domain.ops.runtime_resume = genpd_runtime_resume; 207362306a36Sopenharmony_ci genpd->domain.ops.prepare = genpd_prepare; 207462306a36Sopenharmony_ci genpd->domain.ops.suspend_noirq = genpd_suspend_noirq; 207562306a36Sopenharmony_ci genpd->domain.ops.resume_noirq = genpd_resume_noirq; 207662306a36Sopenharmony_ci genpd->domain.ops.freeze_noirq = genpd_freeze_noirq; 207762306a36Sopenharmony_ci genpd->domain.ops.thaw_noirq = genpd_thaw_noirq; 207862306a36Sopenharmony_ci genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq; 207962306a36Sopenharmony_ci genpd->domain.ops.restore_noirq = genpd_restore_noirq; 208062306a36Sopenharmony_ci genpd->domain.ops.complete = genpd_complete; 208162306a36Sopenharmony_ci genpd->domain.start = genpd_dev_pm_start; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci if (genpd->flags & GENPD_FLAG_PM_CLK) { 208462306a36Sopenharmony_ci genpd->dev_ops.stop = pm_clk_suspend; 208562306a36Sopenharmony_ci genpd->dev_ops.start = pm_clk_resume; 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci /* The always-on governor works better with the corresponding flag. */ 208962306a36Sopenharmony_ci if (gov == &pm_domain_always_on_gov) 209062306a36Sopenharmony_ci genpd->flags |= GENPD_FLAG_RPM_ALWAYS_ON; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci /* Always-on domains must be powered on at initialization. */ 209362306a36Sopenharmony_ci if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) && 209462306a36Sopenharmony_ci !genpd_status_on(genpd)) { 209562306a36Sopenharmony_ci pr_err("always-on PM domain %s is not on\n", genpd->name); 209662306a36Sopenharmony_ci return -EINVAL; 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci /* Multiple states but no governor doesn't make sense. */ 210062306a36Sopenharmony_ci if (!gov && genpd->state_count > 1) 210162306a36Sopenharmony_ci pr_warn("%s: no governor for states\n", genpd->name); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci ret = genpd_alloc_data(genpd); 210462306a36Sopenharmony_ci if (ret) 210562306a36Sopenharmony_ci return ret; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci device_initialize(&genpd->dev); 210862306a36Sopenharmony_ci dev_set_name(&genpd->dev, "%s", genpd->name); 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 211162306a36Sopenharmony_ci list_add(&genpd->gpd_list_node, &gpd_list); 211262306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 211362306a36Sopenharmony_ci genpd_debug_add(genpd); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci return 0; 211662306a36Sopenharmony_ci} 211762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_init); 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_cistatic int genpd_remove(struct generic_pm_domain *genpd) 212062306a36Sopenharmony_ci{ 212162306a36Sopenharmony_ci struct gpd_link *l, *link; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(genpd)) 212462306a36Sopenharmony_ci return -EINVAL; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci genpd_lock(genpd); 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (genpd->has_provider) { 212962306a36Sopenharmony_ci genpd_unlock(genpd); 213062306a36Sopenharmony_ci pr_err("Provider present, unable to remove %s\n", genpd->name); 213162306a36Sopenharmony_ci return -EBUSY; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci if (!list_empty(&genpd->parent_links) || genpd->device_count) { 213562306a36Sopenharmony_ci genpd_unlock(genpd); 213662306a36Sopenharmony_ci pr_err("%s: unable to remove %s\n", __func__, genpd->name); 213762306a36Sopenharmony_ci return -EBUSY; 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci list_for_each_entry_safe(link, l, &genpd->child_links, child_node) { 214162306a36Sopenharmony_ci list_del(&link->parent_node); 214262306a36Sopenharmony_ci list_del(&link->child_node); 214362306a36Sopenharmony_ci kfree(link); 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci list_del(&genpd->gpd_list_node); 214762306a36Sopenharmony_ci genpd_unlock(genpd); 214862306a36Sopenharmony_ci genpd_debug_remove(genpd); 214962306a36Sopenharmony_ci cancel_work_sync(&genpd->power_off_work); 215062306a36Sopenharmony_ci genpd_free_data(genpd); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci pr_debug("%s: removed %s\n", __func__, genpd->name); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci return 0; 215562306a36Sopenharmony_ci} 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci/** 215862306a36Sopenharmony_ci * pm_genpd_remove - Remove a generic I/O PM domain 215962306a36Sopenharmony_ci * @genpd: Pointer to PM domain that is to be removed. 216062306a36Sopenharmony_ci * 216162306a36Sopenharmony_ci * To remove the PM domain, this function: 216262306a36Sopenharmony_ci * - Removes the PM domain as a subdomain to any parent domains, 216362306a36Sopenharmony_ci * if it was added. 216462306a36Sopenharmony_ci * - Removes the PM domain from the list of registered PM domains. 216562306a36Sopenharmony_ci * 216662306a36Sopenharmony_ci * The PM domain will only be removed, if the associated provider has 216762306a36Sopenharmony_ci * been removed, it is not a parent to any other PM domain and has no 216862306a36Sopenharmony_ci * devices associated with it. 216962306a36Sopenharmony_ci */ 217062306a36Sopenharmony_ciint pm_genpd_remove(struct generic_pm_domain *genpd) 217162306a36Sopenharmony_ci{ 217262306a36Sopenharmony_ci int ret; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 217562306a36Sopenharmony_ci ret = genpd_remove(genpd); 217662306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci return ret; 217962306a36Sopenharmony_ci} 218062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_remove); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci#ifdef CONFIG_PM_GENERIC_DOMAINS_OF 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci/* 218562306a36Sopenharmony_ci * Device Tree based PM domain providers. 218662306a36Sopenharmony_ci * 218762306a36Sopenharmony_ci * The code below implements generic device tree based PM domain providers that 218862306a36Sopenharmony_ci * bind device tree nodes with generic PM domains registered in the system. 218962306a36Sopenharmony_ci * 219062306a36Sopenharmony_ci * Any driver that registers generic PM domains and needs to support binding of 219162306a36Sopenharmony_ci * devices to these domains is supposed to register a PM domain provider, which 219262306a36Sopenharmony_ci * maps a PM domain specifier retrieved from the device tree to a PM domain. 219362306a36Sopenharmony_ci * 219462306a36Sopenharmony_ci * Two simple mapping functions have been provided for convenience: 219562306a36Sopenharmony_ci * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 219662306a36Sopenharmony_ci * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 219762306a36Sopenharmony_ci * index. 219862306a36Sopenharmony_ci */ 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci/** 220162306a36Sopenharmony_ci * struct of_genpd_provider - PM domain provider registration structure 220262306a36Sopenharmony_ci * @link: Entry in global list of PM domain providers 220362306a36Sopenharmony_ci * @node: Pointer to device tree node of PM domain provider 220462306a36Sopenharmony_ci * @xlate: Provider-specific xlate callback mapping a set of specifier cells 220562306a36Sopenharmony_ci * into a PM domain. 220662306a36Sopenharmony_ci * @data: context pointer to be passed into @xlate callback 220762306a36Sopenharmony_ci */ 220862306a36Sopenharmony_cistruct of_genpd_provider { 220962306a36Sopenharmony_ci struct list_head link; 221062306a36Sopenharmony_ci struct device_node *node; 221162306a36Sopenharmony_ci genpd_xlate_t xlate; 221262306a36Sopenharmony_ci void *data; 221362306a36Sopenharmony_ci}; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci/* List of registered PM domain providers. */ 221662306a36Sopenharmony_cistatic LIST_HEAD(of_genpd_providers); 221762306a36Sopenharmony_ci/* Mutex to protect the list above. */ 221862306a36Sopenharmony_cistatic DEFINE_MUTEX(of_genpd_mutex); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci/** 222162306a36Sopenharmony_ci * genpd_xlate_simple() - Xlate function for direct node-domain mapping 222262306a36Sopenharmony_ci * @genpdspec: OF phandle args to map into a PM domain 222362306a36Sopenharmony_ci * @data: xlate function private data - pointer to struct generic_pm_domain 222462306a36Sopenharmony_ci * 222562306a36Sopenharmony_ci * This is a generic xlate function that can be used to model PM domains that 222662306a36Sopenharmony_ci * have their own device tree nodes. The private data of xlate function needs 222762306a36Sopenharmony_ci * to be a valid pointer to struct generic_pm_domain. 222862306a36Sopenharmony_ci */ 222962306a36Sopenharmony_cistatic struct generic_pm_domain *genpd_xlate_simple( 223062306a36Sopenharmony_ci struct of_phandle_args *genpdspec, 223162306a36Sopenharmony_ci void *data) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci return data; 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci/** 223762306a36Sopenharmony_ci * genpd_xlate_onecell() - Xlate function using a single index. 223862306a36Sopenharmony_ci * @genpdspec: OF phandle args to map into a PM domain 223962306a36Sopenharmony_ci * @data: xlate function private data - pointer to struct genpd_onecell_data 224062306a36Sopenharmony_ci * 224162306a36Sopenharmony_ci * This is a generic xlate function that can be used to model simple PM domain 224262306a36Sopenharmony_ci * controllers that have one device tree node and provide multiple PM domains. 224362306a36Sopenharmony_ci * A single cell is used as an index into an array of PM domains specified in 224462306a36Sopenharmony_ci * the genpd_onecell_data struct when registering the provider. 224562306a36Sopenharmony_ci */ 224662306a36Sopenharmony_cistatic struct generic_pm_domain *genpd_xlate_onecell( 224762306a36Sopenharmony_ci struct of_phandle_args *genpdspec, 224862306a36Sopenharmony_ci void *data) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct genpd_onecell_data *genpd_data = data; 225162306a36Sopenharmony_ci unsigned int idx = genpdspec->args[0]; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci if (genpdspec->args_count != 1) 225462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci if (idx >= genpd_data->num_domains) { 225762306a36Sopenharmony_ci pr_err("%s: invalid domain index %u\n", __func__, idx); 225862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci if (!genpd_data->domains[idx]) 226262306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci return genpd_data->domains[idx]; 226562306a36Sopenharmony_ci} 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci/** 226862306a36Sopenharmony_ci * genpd_add_provider() - Register a PM domain provider for a node 226962306a36Sopenharmony_ci * @np: Device node pointer associated with the PM domain provider. 227062306a36Sopenharmony_ci * @xlate: Callback for decoding PM domain from phandle arguments. 227162306a36Sopenharmony_ci * @data: Context pointer for @xlate callback. 227262306a36Sopenharmony_ci */ 227362306a36Sopenharmony_cistatic int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 227462306a36Sopenharmony_ci void *data) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci struct of_genpd_provider *cp; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci cp = kzalloc(sizeof(*cp), GFP_KERNEL); 227962306a36Sopenharmony_ci if (!cp) 228062306a36Sopenharmony_ci return -ENOMEM; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci cp->node = of_node_get(np); 228362306a36Sopenharmony_ci cp->data = data; 228462306a36Sopenharmony_ci cp->xlate = xlate; 228562306a36Sopenharmony_ci fwnode_dev_initialized(&np->fwnode, true); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci mutex_lock(&of_genpd_mutex); 228862306a36Sopenharmony_ci list_add(&cp->link, &of_genpd_providers); 228962306a36Sopenharmony_ci mutex_unlock(&of_genpd_mutex); 229062306a36Sopenharmony_ci pr_debug("Added domain provider from %pOF\n", np); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci return 0; 229362306a36Sopenharmony_ci} 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_cistatic bool genpd_present(const struct generic_pm_domain *genpd) 229662306a36Sopenharmony_ci{ 229762306a36Sopenharmony_ci bool ret = false; 229862306a36Sopenharmony_ci const struct generic_pm_domain *gpd; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 230162306a36Sopenharmony_ci list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 230262306a36Sopenharmony_ci if (gpd == genpd) { 230362306a36Sopenharmony_ci ret = true; 230462306a36Sopenharmony_ci break; 230562306a36Sopenharmony_ci } 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci return ret; 231062306a36Sopenharmony_ci} 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci/** 231362306a36Sopenharmony_ci * of_genpd_add_provider_simple() - Register a simple PM domain provider 231462306a36Sopenharmony_ci * @np: Device node pointer associated with the PM domain provider. 231562306a36Sopenharmony_ci * @genpd: Pointer to PM domain associated with the PM domain provider. 231662306a36Sopenharmony_ci */ 231762306a36Sopenharmony_ciint of_genpd_add_provider_simple(struct device_node *np, 231862306a36Sopenharmony_ci struct generic_pm_domain *genpd) 231962306a36Sopenharmony_ci{ 232062306a36Sopenharmony_ci int ret; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci if (!np || !genpd) 232362306a36Sopenharmony_ci return -EINVAL; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci if (!genpd_present(genpd)) 232662306a36Sopenharmony_ci return -EINVAL; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci genpd->dev.of_node = np; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci /* Parse genpd OPP table */ 233162306a36Sopenharmony_ci if (genpd->set_performance_state) { 233262306a36Sopenharmony_ci ret = dev_pm_opp_of_add_table(&genpd->dev); 233362306a36Sopenharmony_ci if (ret) 233462306a36Sopenharmony_ci return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n"); 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci /* 233762306a36Sopenharmony_ci * Save table for faster processing while setting performance 233862306a36Sopenharmony_ci * state. 233962306a36Sopenharmony_ci */ 234062306a36Sopenharmony_ci genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev); 234162306a36Sopenharmony_ci WARN_ON(IS_ERR(genpd->opp_table)); 234262306a36Sopenharmony_ci } 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 234562306a36Sopenharmony_ci if (ret) { 234662306a36Sopenharmony_ci if (genpd->set_performance_state) { 234762306a36Sopenharmony_ci dev_pm_opp_put_opp_table(genpd->opp_table); 234862306a36Sopenharmony_ci dev_pm_opp_of_remove_table(&genpd->dev); 234962306a36Sopenharmony_ci } 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci return ret; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci genpd->provider = &np->fwnode; 235562306a36Sopenharmony_ci genpd->has_provider = true; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci return 0; 235862306a36Sopenharmony_ci} 235962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci/** 236262306a36Sopenharmony_ci * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 236362306a36Sopenharmony_ci * @np: Device node pointer associated with the PM domain provider. 236462306a36Sopenharmony_ci * @data: Pointer to the data associated with the PM domain provider. 236562306a36Sopenharmony_ci */ 236662306a36Sopenharmony_ciint of_genpd_add_provider_onecell(struct device_node *np, 236762306a36Sopenharmony_ci struct genpd_onecell_data *data) 236862306a36Sopenharmony_ci{ 236962306a36Sopenharmony_ci struct generic_pm_domain *genpd; 237062306a36Sopenharmony_ci unsigned int i; 237162306a36Sopenharmony_ci int ret = -EINVAL; 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci if (!np || !data) 237462306a36Sopenharmony_ci return -EINVAL; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci if (!data->xlate) 237762306a36Sopenharmony_ci data->xlate = genpd_xlate_onecell; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci for (i = 0; i < data->num_domains; i++) { 238062306a36Sopenharmony_ci genpd = data->domains[i]; 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (!genpd) 238362306a36Sopenharmony_ci continue; 238462306a36Sopenharmony_ci if (!genpd_present(genpd)) 238562306a36Sopenharmony_ci goto error; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci genpd->dev.of_node = np; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci /* Parse genpd OPP table */ 239062306a36Sopenharmony_ci if (genpd->set_performance_state) { 239162306a36Sopenharmony_ci ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); 239262306a36Sopenharmony_ci if (ret) { 239362306a36Sopenharmony_ci dev_err_probe(&genpd->dev, ret, 239462306a36Sopenharmony_ci "Failed to add OPP table for index %d\n", i); 239562306a36Sopenharmony_ci goto error; 239662306a36Sopenharmony_ci } 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci /* 239962306a36Sopenharmony_ci * Save table for faster processing while setting 240062306a36Sopenharmony_ci * performance state. 240162306a36Sopenharmony_ci */ 240262306a36Sopenharmony_ci genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev); 240362306a36Sopenharmony_ci WARN_ON(IS_ERR(genpd->opp_table)); 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci genpd->provider = &np->fwnode; 240762306a36Sopenharmony_ci genpd->has_provider = true; 240862306a36Sopenharmony_ci } 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci ret = genpd_add_provider(np, data->xlate, data); 241162306a36Sopenharmony_ci if (ret < 0) 241262306a36Sopenharmony_ci goto error; 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci return 0; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cierror: 241762306a36Sopenharmony_ci while (i--) { 241862306a36Sopenharmony_ci genpd = data->domains[i]; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci if (!genpd) 242162306a36Sopenharmony_ci continue; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci genpd->provider = NULL; 242462306a36Sopenharmony_ci genpd->has_provider = false; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci if (genpd->set_performance_state) { 242762306a36Sopenharmony_ci dev_pm_opp_put_opp_table(genpd->opp_table); 242862306a36Sopenharmony_ci dev_pm_opp_of_remove_table(&genpd->dev); 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci } 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci return ret; 243362306a36Sopenharmony_ci} 243462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci/** 243762306a36Sopenharmony_ci * of_genpd_del_provider() - Remove a previously registered PM domain provider 243862306a36Sopenharmony_ci * @np: Device node pointer associated with the PM domain provider 243962306a36Sopenharmony_ci */ 244062306a36Sopenharmony_civoid of_genpd_del_provider(struct device_node *np) 244162306a36Sopenharmony_ci{ 244262306a36Sopenharmony_ci struct of_genpd_provider *cp, *tmp; 244362306a36Sopenharmony_ci struct generic_pm_domain *gpd; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 244662306a36Sopenharmony_ci mutex_lock(&of_genpd_mutex); 244762306a36Sopenharmony_ci list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) { 244862306a36Sopenharmony_ci if (cp->node == np) { 244962306a36Sopenharmony_ci /* 245062306a36Sopenharmony_ci * For each PM domain associated with the 245162306a36Sopenharmony_ci * provider, set the 'has_provider' to false 245262306a36Sopenharmony_ci * so that the PM domain can be safely removed. 245362306a36Sopenharmony_ci */ 245462306a36Sopenharmony_ci list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 245562306a36Sopenharmony_ci if (gpd->provider == &np->fwnode) { 245662306a36Sopenharmony_ci gpd->has_provider = false; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci if (!gpd->set_performance_state) 245962306a36Sopenharmony_ci continue; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci dev_pm_opp_put_opp_table(gpd->opp_table); 246262306a36Sopenharmony_ci dev_pm_opp_of_remove_table(&gpd->dev); 246362306a36Sopenharmony_ci } 246462306a36Sopenharmony_ci } 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci fwnode_dev_initialized(&cp->node->fwnode, false); 246762306a36Sopenharmony_ci list_del(&cp->link); 246862306a36Sopenharmony_ci of_node_put(cp->node); 246962306a36Sopenharmony_ci kfree(cp); 247062306a36Sopenharmony_ci break; 247162306a36Sopenharmony_ci } 247262306a36Sopenharmony_ci } 247362306a36Sopenharmony_ci mutex_unlock(&of_genpd_mutex); 247462306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 247562306a36Sopenharmony_ci} 247662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_del_provider); 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci/** 247962306a36Sopenharmony_ci * genpd_get_from_provider() - Look-up PM domain 248062306a36Sopenharmony_ci * @genpdspec: OF phandle args to use for look-up 248162306a36Sopenharmony_ci * 248262306a36Sopenharmony_ci * Looks for a PM domain provider under the node specified by @genpdspec and if 248362306a36Sopenharmony_ci * found, uses xlate function of the provider to map phandle args to a PM 248462306a36Sopenharmony_ci * domain. 248562306a36Sopenharmony_ci * 248662306a36Sopenharmony_ci * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 248762306a36Sopenharmony_ci * on failure. 248862306a36Sopenharmony_ci */ 248962306a36Sopenharmony_cistatic struct generic_pm_domain *genpd_get_from_provider( 249062306a36Sopenharmony_ci struct of_phandle_args *genpdspec) 249162306a36Sopenharmony_ci{ 249262306a36Sopenharmony_ci struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 249362306a36Sopenharmony_ci struct of_genpd_provider *provider; 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci if (!genpdspec) 249662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci mutex_lock(&of_genpd_mutex); 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci /* Check if we have such a provider in our array */ 250162306a36Sopenharmony_ci list_for_each_entry(provider, &of_genpd_providers, link) { 250262306a36Sopenharmony_ci if (provider->node == genpdspec->np) 250362306a36Sopenharmony_ci genpd = provider->xlate(genpdspec, provider->data); 250462306a36Sopenharmony_ci if (!IS_ERR(genpd)) 250562306a36Sopenharmony_ci break; 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci mutex_unlock(&of_genpd_mutex); 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci return genpd; 251162306a36Sopenharmony_ci} 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci/** 251462306a36Sopenharmony_ci * of_genpd_add_device() - Add a device to an I/O PM domain 251562306a36Sopenharmony_ci * @genpdspec: OF phandle args to use for look-up PM domain 251662306a36Sopenharmony_ci * @dev: Device to be added. 251762306a36Sopenharmony_ci * 251862306a36Sopenharmony_ci * Looks-up an I/O PM domain based upon phandle args provided and adds 251962306a36Sopenharmony_ci * the device to the PM domain. Returns a negative error code on failure. 252062306a36Sopenharmony_ci */ 252162306a36Sopenharmony_ciint of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 252262306a36Sopenharmony_ci{ 252362306a36Sopenharmony_ci struct generic_pm_domain *genpd; 252462306a36Sopenharmony_ci int ret; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci if (!dev) 252762306a36Sopenharmony_ci return -EINVAL; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci genpd = genpd_get_from_provider(genpdspec); 253262306a36Sopenharmony_ci if (IS_ERR(genpd)) { 253362306a36Sopenharmony_ci ret = PTR_ERR(genpd); 253462306a36Sopenharmony_ci goto out; 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci ret = genpd_add_device(genpd, dev, dev); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ciout: 254062306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci return ret; 254362306a36Sopenharmony_ci} 254462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_add_device); 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci/** 254762306a36Sopenharmony_ci * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 254862306a36Sopenharmony_ci * @parent_spec: OF phandle args to use for parent PM domain look-up 254962306a36Sopenharmony_ci * @subdomain_spec: OF phandle args to use for subdomain look-up 255062306a36Sopenharmony_ci * 255162306a36Sopenharmony_ci * Looks-up a parent PM domain and subdomain based upon phandle args 255262306a36Sopenharmony_ci * provided and adds the subdomain to the parent PM domain. Returns a 255362306a36Sopenharmony_ci * negative error code on failure. 255462306a36Sopenharmony_ci */ 255562306a36Sopenharmony_ciint of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 255662306a36Sopenharmony_ci struct of_phandle_args *subdomain_spec) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci struct generic_pm_domain *parent, *subdomain; 255962306a36Sopenharmony_ci int ret; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci parent = genpd_get_from_provider(parent_spec); 256462306a36Sopenharmony_ci if (IS_ERR(parent)) { 256562306a36Sopenharmony_ci ret = PTR_ERR(parent); 256662306a36Sopenharmony_ci goto out; 256762306a36Sopenharmony_ci } 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci subdomain = genpd_get_from_provider(subdomain_spec); 257062306a36Sopenharmony_ci if (IS_ERR(subdomain)) { 257162306a36Sopenharmony_ci ret = PTR_ERR(subdomain); 257262306a36Sopenharmony_ci goto out; 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci ret = genpd_add_subdomain(parent, subdomain); 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ciout: 257862306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci return ret == -ENOENT ? -EPROBE_DEFER : ret; 258162306a36Sopenharmony_ci} 258262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci/** 258562306a36Sopenharmony_ci * of_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 258662306a36Sopenharmony_ci * @parent_spec: OF phandle args to use for parent PM domain look-up 258762306a36Sopenharmony_ci * @subdomain_spec: OF phandle args to use for subdomain look-up 258862306a36Sopenharmony_ci * 258962306a36Sopenharmony_ci * Looks-up a parent PM domain and subdomain based upon phandle args 259062306a36Sopenharmony_ci * provided and removes the subdomain from the parent PM domain. Returns a 259162306a36Sopenharmony_ci * negative error code on failure. 259262306a36Sopenharmony_ci */ 259362306a36Sopenharmony_ciint of_genpd_remove_subdomain(struct of_phandle_args *parent_spec, 259462306a36Sopenharmony_ci struct of_phandle_args *subdomain_spec) 259562306a36Sopenharmony_ci{ 259662306a36Sopenharmony_ci struct generic_pm_domain *parent, *subdomain; 259762306a36Sopenharmony_ci int ret; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci parent = genpd_get_from_provider(parent_spec); 260262306a36Sopenharmony_ci if (IS_ERR(parent)) { 260362306a36Sopenharmony_ci ret = PTR_ERR(parent); 260462306a36Sopenharmony_ci goto out; 260562306a36Sopenharmony_ci } 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci subdomain = genpd_get_from_provider(subdomain_spec); 260862306a36Sopenharmony_ci if (IS_ERR(subdomain)) { 260962306a36Sopenharmony_ci ret = PTR_ERR(subdomain); 261062306a36Sopenharmony_ci goto out; 261162306a36Sopenharmony_ci } 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci ret = pm_genpd_remove_subdomain(parent, subdomain); 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ciout: 261662306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci return ret; 261962306a36Sopenharmony_ci} 262062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_remove_subdomain); 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci/** 262362306a36Sopenharmony_ci * of_genpd_remove_last - Remove the last PM domain registered for a provider 262462306a36Sopenharmony_ci * @np: Pointer to device node associated with provider 262562306a36Sopenharmony_ci * 262662306a36Sopenharmony_ci * Find the last PM domain that was added by a particular provider and 262762306a36Sopenharmony_ci * remove this PM domain from the list of PM domains. The provider is 262862306a36Sopenharmony_ci * identified by the 'provider' device structure that is passed. The PM 262962306a36Sopenharmony_ci * domain will only be removed, if the provider associated with domain 263062306a36Sopenharmony_ci * has been removed. 263162306a36Sopenharmony_ci * 263262306a36Sopenharmony_ci * Returns a valid pointer to struct generic_pm_domain on success or 263362306a36Sopenharmony_ci * ERR_PTR() on failure. 263462306a36Sopenharmony_ci */ 263562306a36Sopenharmony_cistruct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT); 263862306a36Sopenharmony_ci int ret; 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(np)) 264162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 264462306a36Sopenharmony_ci list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { 264562306a36Sopenharmony_ci if (gpd->provider == &np->fwnode) { 264662306a36Sopenharmony_ci ret = genpd_remove(gpd); 264762306a36Sopenharmony_ci genpd = ret ? ERR_PTR(ret) : gpd; 264862306a36Sopenharmony_ci break; 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci return genpd; 265462306a36Sopenharmony_ci} 265562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_remove_last); 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_cistatic void genpd_release_dev(struct device *dev) 265862306a36Sopenharmony_ci{ 265962306a36Sopenharmony_ci of_node_put(dev->of_node); 266062306a36Sopenharmony_ci kfree(dev); 266162306a36Sopenharmony_ci} 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_cistatic struct bus_type genpd_bus_type = { 266462306a36Sopenharmony_ci .name = "genpd", 266562306a36Sopenharmony_ci}; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci/** 266862306a36Sopenharmony_ci * genpd_dev_pm_detach - Detach a device from its PM domain. 266962306a36Sopenharmony_ci * @dev: Device to detach. 267062306a36Sopenharmony_ci * @power_off: Currently not used 267162306a36Sopenharmony_ci * 267262306a36Sopenharmony_ci * Try to locate a corresponding generic PM domain, which the device was 267362306a36Sopenharmony_ci * attached to previously. If such is found, the device is detached from it. 267462306a36Sopenharmony_ci */ 267562306a36Sopenharmony_cistatic void genpd_dev_pm_detach(struct device *dev, bool power_off) 267662306a36Sopenharmony_ci{ 267762306a36Sopenharmony_ci struct generic_pm_domain *pd; 267862306a36Sopenharmony_ci unsigned int i; 267962306a36Sopenharmony_ci int ret = 0; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci pd = dev_to_genpd(dev); 268262306a36Sopenharmony_ci if (IS_ERR(pd)) 268362306a36Sopenharmony_ci return; 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci dev_dbg(dev, "removing from PM domain %s\n", pd->name); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci /* Drop the default performance state */ 268862306a36Sopenharmony_ci if (dev_gpd_data(dev)->default_pstate) { 268962306a36Sopenharmony_ci dev_pm_genpd_set_performance_state(dev, 0); 269062306a36Sopenharmony_ci dev_gpd_data(dev)->default_pstate = 0; 269162306a36Sopenharmony_ci } 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 269462306a36Sopenharmony_ci ret = genpd_remove_device(pd, dev); 269562306a36Sopenharmony_ci if (ret != -EAGAIN) 269662306a36Sopenharmony_ci break; 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci mdelay(i); 269962306a36Sopenharmony_ci cond_resched(); 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci if (ret < 0) { 270362306a36Sopenharmony_ci dev_err(dev, "failed to remove from PM domain %s: %d", 270462306a36Sopenharmony_ci pd->name, ret); 270562306a36Sopenharmony_ci return; 270662306a36Sopenharmony_ci } 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci /* Check if PM domain can be powered off after removing this device. */ 270962306a36Sopenharmony_ci genpd_queue_power_off_work(pd); 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci /* Unregister the device if it was created by genpd. */ 271262306a36Sopenharmony_ci if (dev->bus == &genpd_bus_type) 271362306a36Sopenharmony_ci device_unregister(dev); 271462306a36Sopenharmony_ci} 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_cistatic void genpd_dev_pm_sync(struct device *dev) 271762306a36Sopenharmony_ci{ 271862306a36Sopenharmony_ci struct generic_pm_domain *pd; 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci pd = dev_to_genpd(dev); 272162306a36Sopenharmony_ci if (IS_ERR(pd)) 272262306a36Sopenharmony_ci return; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci genpd_queue_power_off_work(pd); 272562306a36Sopenharmony_ci} 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_cistatic int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, 272862306a36Sopenharmony_ci unsigned int index, bool power_on) 272962306a36Sopenharmony_ci{ 273062306a36Sopenharmony_ci struct of_phandle_args pd_args; 273162306a36Sopenharmony_ci struct generic_pm_domain *pd; 273262306a36Sopenharmony_ci int pstate; 273362306a36Sopenharmony_ci int ret; 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 273662306a36Sopenharmony_ci "#power-domain-cells", index, &pd_args); 273762306a36Sopenharmony_ci if (ret < 0) 273862306a36Sopenharmony_ci return ret; 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci mutex_lock(&gpd_list_lock); 274162306a36Sopenharmony_ci pd = genpd_get_from_provider(&pd_args); 274262306a36Sopenharmony_ci of_node_put(pd_args.np); 274362306a36Sopenharmony_ci if (IS_ERR(pd)) { 274462306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 274562306a36Sopenharmony_ci dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 274662306a36Sopenharmony_ci __func__, PTR_ERR(pd)); 274762306a36Sopenharmony_ci return driver_deferred_probe_check_state(base_dev); 274862306a36Sopenharmony_ci } 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci dev_dbg(dev, "adding to PM domain %s\n", pd->name); 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci ret = genpd_add_device(pd, dev, base_dev); 275362306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci if (ret < 0) 275662306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to add to PM domain %s\n", pd->name); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci dev->pm_domain->detach = genpd_dev_pm_detach; 275962306a36Sopenharmony_ci dev->pm_domain->sync = genpd_dev_pm_sync; 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci /* Set the default performance state */ 276262306a36Sopenharmony_ci pstate = of_get_required_opp_performance_state(dev->of_node, index); 276362306a36Sopenharmony_ci if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) { 276462306a36Sopenharmony_ci ret = pstate; 276562306a36Sopenharmony_ci goto err; 276662306a36Sopenharmony_ci } else if (pstate > 0) { 276762306a36Sopenharmony_ci ret = dev_pm_genpd_set_performance_state(dev, pstate); 276862306a36Sopenharmony_ci if (ret) 276962306a36Sopenharmony_ci goto err; 277062306a36Sopenharmony_ci dev_gpd_data(dev)->default_pstate = pstate; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (power_on) { 277462306a36Sopenharmony_ci genpd_lock(pd); 277562306a36Sopenharmony_ci ret = genpd_power_on(pd, 0); 277662306a36Sopenharmony_ci genpd_unlock(pd); 277762306a36Sopenharmony_ci } 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci if (ret) { 278062306a36Sopenharmony_ci /* Drop the default performance state */ 278162306a36Sopenharmony_ci if (dev_gpd_data(dev)->default_pstate) { 278262306a36Sopenharmony_ci dev_pm_genpd_set_performance_state(dev, 0); 278362306a36Sopenharmony_ci dev_gpd_data(dev)->default_pstate = 0; 278462306a36Sopenharmony_ci } 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci genpd_remove_device(pd, dev); 278762306a36Sopenharmony_ci return -EPROBE_DEFER; 278862306a36Sopenharmony_ci } 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci return 1; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_cierr: 279362306a36Sopenharmony_ci dev_err(dev, "failed to set required performance state for power-domain %s: %d\n", 279462306a36Sopenharmony_ci pd->name, ret); 279562306a36Sopenharmony_ci genpd_remove_device(pd, dev); 279662306a36Sopenharmony_ci return ret; 279762306a36Sopenharmony_ci} 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci/** 280062306a36Sopenharmony_ci * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 280162306a36Sopenharmony_ci * @dev: Device to attach. 280262306a36Sopenharmony_ci * 280362306a36Sopenharmony_ci * Parse device's OF node to find a PM domain specifier. If such is found, 280462306a36Sopenharmony_ci * attaches the device to retrieved pm_domain ops. 280562306a36Sopenharmony_ci * 280662306a36Sopenharmony_ci * Returns 1 on successfully attached PM domain, 0 when the device don't need a 280762306a36Sopenharmony_ci * PM domain or when multiple power-domains exists for it, else a negative error 280862306a36Sopenharmony_ci * code. Note that if a power-domain exists for the device, but it cannot be 280962306a36Sopenharmony_ci * found or turned on, then return -EPROBE_DEFER to ensure that the device is 281062306a36Sopenharmony_ci * not probed and to re-try again later. 281162306a36Sopenharmony_ci */ 281262306a36Sopenharmony_ciint genpd_dev_pm_attach(struct device *dev) 281362306a36Sopenharmony_ci{ 281462306a36Sopenharmony_ci if (!dev->of_node) 281562306a36Sopenharmony_ci return 0; 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* 281862306a36Sopenharmony_ci * Devices with multiple PM domains must be attached separately, as we 281962306a36Sopenharmony_ci * can only attach one PM domain per device. 282062306a36Sopenharmony_ci */ 282162306a36Sopenharmony_ci if (of_count_phandle_with_args(dev->of_node, "power-domains", 282262306a36Sopenharmony_ci "#power-domain-cells") != 1) 282362306a36Sopenharmony_ci return 0; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci return __genpd_dev_pm_attach(dev, dev, 0, true); 282662306a36Sopenharmony_ci} 282762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci/** 283062306a36Sopenharmony_ci * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains. 283162306a36Sopenharmony_ci * @dev: The device used to lookup the PM domain. 283262306a36Sopenharmony_ci * @index: The index of the PM domain. 283362306a36Sopenharmony_ci * 283462306a36Sopenharmony_ci * Parse device's OF node to find a PM domain specifier at the provided @index. 283562306a36Sopenharmony_ci * If such is found, creates a virtual device and attaches it to the retrieved 283662306a36Sopenharmony_ci * pm_domain ops. To deal with detaching of the virtual device, the ->detach() 283762306a36Sopenharmony_ci * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach(). 283862306a36Sopenharmony_ci * 283962306a36Sopenharmony_ci * Returns the created virtual device if successfully attached PM domain, NULL 284062306a36Sopenharmony_ci * when the device don't need a PM domain, else an ERR_PTR() in case of 284162306a36Sopenharmony_ci * failures. If a power-domain exists for the device, but cannot be found or 284262306a36Sopenharmony_ci * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device 284362306a36Sopenharmony_ci * is not probed and to re-try again later. 284462306a36Sopenharmony_ci */ 284562306a36Sopenharmony_cistruct device *genpd_dev_pm_attach_by_id(struct device *dev, 284662306a36Sopenharmony_ci unsigned int index) 284762306a36Sopenharmony_ci{ 284862306a36Sopenharmony_ci struct device *virt_dev; 284962306a36Sopenharmony_ci int num_domains; 285062306a36Sopenharmony_ci int ret; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci if (!dev->of_node) 285362306a36Sopenharmony_ci return NULL; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci /* Verify that the index is within a valid range. */ 285662306a36Sopenharmony_ci num_domains = of_count_phandle_with_args(dev->of_node, "power-domains", 285762306a36Sopenharmony_ci "#power-domain-cells"); 285862306a36Sopenharmony_ci if (index >= num_domains) 285962306a36Sopenharmony_ci return NULL; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci /* Allocate and register device on the genpd bus. */ 286262306a36Sopenharmony_ci virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL); 286362306a36Sopenharmony_ci if (!virt_dev) 286462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev)); 286762306a36Sopenharmony_ci virt_dev->bus = &genpd_bus_type; 286862306a36Sopenharmony_ci virt_dev->release = genpd_release_dev; 286962306a36Sopenharmony_ci virt_dev->of_node = of_node_get(dev->of_node); 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci ret = device_register(virt_dev); 287262306a36Sopenharmony_ci if (ret) { 287362306a36Sopenharmony_ci put_device(virt_dev); 287462306a36Sopenharmony_ci return ERR_PTR(ret); 287562306a36Sopenharmony_ci } 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci /* Try to attach the device to the PM domain at the specified index. */ 287862306a36Sopenharmony_ci ret = __genpd_dev_pm_attach(virt_dev, dev, index, false); 287962306a36Sopenharmony_ci if (ret < 1) { 288062306a36Sopenharmony_ci device_unregister(virt_dev); 288162306a36Sopenharmony_ci return ret ? ERR_PTR(ret) : NULL; 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci pm_runtime_enable(virt_dev); 288562306a36Sopenharmony_ci genpd_queue_power_off_work(dev_to_genpd(virt_dev)); 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci return virt_dev; 288862306a36Sopenharmony_ci} 288962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id); 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci/** 289262306a36Sopenharmony_ci * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains. 289362306a36Sopenharmony_ci * @dev: The device used to lookup the PM domain. 289462306a36Sopenharmony_ci * @name: The name of the PM domain. 289562306a36Sopenharmony_ci * 289662306a36Sopenharmony_ci * Parse device's OF node to find a PM domain specifier using the 289762306a36Sopenharmony_ci * power-domain-names DT property. For further description see 289862306a36Sopenharmony_ci * genpd_dev_pm_attach_by_id(). 289962306a36Sopenharmony_ci */ 290062306a36Sopenharmony_cistruct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name) 290162306a36Sopenharmony_ci{ 290262306a36Sopenharmony_ci int index; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci if (!dev->of_node) 290562306a36Sopenharmony_ci return NULL; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci index = of_property_match_string(dev->of_node, "power-domain-names", 290862306a36Sopenharmony_ci name); 290962306a36Sopenharmony_ci if (index < 0) 291062306a36Sopenharmony_ci return NULL; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci return genpd_dev_pm_attach_by_id(dev, index); 291362306a36Sopenharmony_ci} 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_cistatic const struct of_device_id idle_state_match[] = { 291662306a36Sopenharmony_ci { .compatible = "domain-idle-state", }, 291762306a36Sopenharmony_ci { } 291862306a36Sopenharmony_ci}; 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_cistatic int genpd_parse_state(struct genpd_power_state *genpd_state, 292162306a36Sopenharmony_ci struct device_node *state_node) 292262306a36Sopenharmony_ci{ 292362306a36Sopenharmony_ci int err; 292462306a36Sopenharmony_ci u32 residency; 292562306a36Sopenharmony_ci u32 entry_latency, exit_latency; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci err = of_property_read_u32(state_node, "entry-latency-us", 292862306a36Sopenharmony_ci &entry_latency); 292962306a36Sopenharmony_ci if (err) { 293062306a36Sopenharmony_ci pr_debug(" * %pOF missing entry-latency-us property\n", 293162306a36Sopenharmony_ci state_node); 293262306a36Sopenharmony_ci return -EINVAL; 293362306a36Sopenharmony_ci } 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci err = of_property_read_u32(state_node, "exit-latency-us", 293662306a36Sopenharmony_ci &exit_latency); 293762306a36Sopenharmony_ci if (err) { 293862306a36Sopenharmony_ci pr_debug(" * %pOF missing exit-latency-us property\n", 293962306a36Sopenharmony_ci state_node); 294062306a36Sopenharmony_ci return -EINVAL; 294162306a36Sopenharmony_ci } 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci err = of_property_read_u32(state_node, "min-residency-us", &residency); 294462306a36Sopenharmony_ci if (!err) 294562306a36Sopenharmony_ci genpd_state->residency_ns = 1000LL * residency; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci genpd_state->power_on_latency_ns = 1000LL * exit_latency; 294862306a36Sopenharmony_ci genpd_state->power_off_latency_ns = 1000LL * entry_latency; 294962306a36Sopenharmony_ci genpd_state->fwnode = &state_node->fwnode; 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci return 0; 295262306a36Sopenharmony_ci} 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_cistatic int genpd_iterate_idle_states(struct device_node *dn, 295562306a36Sopenharmony_ci struct genpd_power_state *states) 295662306a36Sopenharmony_ci{ 295762306a36Sopenharmony_ci int ret; 295862306a36Sopenharmony_ci struct of_phandle_iterator it; 295962306a36Sopenharmony_ci struct device_node *np; 296062306a36Sopenharmony_ci int i = 0; 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ci ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 296362306a36Sopenharmony_ci if (ret <= 0) 296462306a36Sopenharmony_ci return ret == -ENOENT ? 0 : ret; 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci /* Loop over the phandles until all the requested entry is found */ 296762306a36Sopenharmony_ci of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) { 296862306a36Sopenharmony_ci np = it.node; 296962306a36Sopenharmony_ci if (!of_match_node(idle_state_match, np)) 297062306a36Sopenharmony_ci continue; 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci if (!of_device_is_available(np)) 297362306a36Sopenharmony_ci continue; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci if (states) { 297662306a36Sopenharmony_ci ret = genpd_parse_state(&states[i], np); 297762306a36Sopenharmony_ci if (ret) { 297862306a36Sopenharmony_ci pr_err("Parsing idle state node %pOF failed with err %d\n", 297962306a36Sopenharmony_ci np, ret); 298062306a36Sopenharmony_ci of_node_put(np); 298162306a36Sopenharmony_ci return ret; 298262306a36Sopenharmony_ci } 298362306a36Sopenharmony_ci } 298462306a36Sopenharmony_ci i++; 298562306a36Sopenharmony_ci } 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci return i; 298862306a36Sopenharmony_ci} 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci/** 299162306a36Sopenharmony_ci * of_genpd_parse_idle_states: Return array of idle states for the genpd. 299262306a36Sopenharmony_ci * 299362306a36Sopenharmony_ci * @dn: The genpd device node 299462306a36Sopenharmony_ci * @states: The pointer to which the state array will be saved. 299562306a36Sopenharmony_ci * @n: The count of elements in the array returned from this function. 299662306a36Sopenharmony_ci * 299762306a36Sopenharmony_ci * Returns the device states parsed from the OF node. The memory for the states 299862306a36Sopenharmony_ci * is allocated by this function and is the responsibility of the caller to 299962306a36Sopenharmony_ci * free the memory after use. If any or zero compatible domain idle states is 300062306a36Sopenharmony_ci * found it returns 0 and in case of errors, a negative error code is returned. 300162306a36Sopenharmony_ci */ 300262306a36Sopenharmony_ciint of_genpd_parse_idle_states(struct device_node *dn, 300362306a36Sopenharmony_ci struct genpd_power_state **states, int *n) 300462306a36Sopenharmony_ci{ 300562306a36Sopenharmony_ci struct genpd_power_state *st; 300662306a36Sopenharmony_ci int ret; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci ret = genpd_iterate_idle_states(dn, NULL); 300962306a36Sopenharmony_ci if (ret < 0) 301062306a36Sopenharmony_ci return ret; 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci if (!ret) { 301362306a36Sopenharmony_ci *states = NULL; 301462306a36Sopenharmony_ci *n = 0; 301562306a36Sopenharmony_ci return 0; 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci st = kcalloc(ret, sizeof(*st), GFP_KERNEL); 301962306a36Sopenharmony_ci if (!st) 302062306a36Sopenharmony_ci return -ENOMEM; 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci ret = genpd_iterate_idle_states(dn, st); 302362306a36Sopenharmony_ci if (ret <= 0) { 302462306a36Sopenharmony_ci kfree(st); 302562306a36Sopenharmony_ci return ret < 0 ? ret : -EINVAL; 302662306a36Sopenharmony_ci } 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci *states = st; 302962306a36Sopenharmony_ci *n = ret; 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci return 0; 303262306a36Sopenharmony_ci} 303362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci/** 303662306a36Sopenharmony_ci * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node. 303762306a36Sopenharmony_ci * 303862306a36Sopenharmony_ci * @genpd_dev: Genpd's device for which the performance-state needs to be found. 303962306a36Sopenharmony_ci * @opp: struct dev_pm_opp of the OPP for which we need to find performance 304062306a36Sopenharmony_ci * state. 304162306a36Sopenharmony_ci * 304262306a36Sopenharmony_ci * Returns performance state encoded in the OPP of the genpd. This calls 304362306a36Sopenharmony_ci * platform specific genpd->opp_to_performance_state() callback to translate 304462306a36Sopenharmony_ci * power domain OPP to performance state. 304562306a36Sopenharmony_ci * 304662306a36Sopenharmony_ci * Returns performance state on success and 0 on failure. 304762306a36Sopenharmony_ci */ 304862306a36Sopenharmony_ciunsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev, 304962306a36Sopenharmony_ci struct dev_pm_opp *opp) 305062306a36Sopenharmony_ci{ 305162306a36Sopenharmony_ci struct generic_pm_domain *genpd = NULL; 305262306a36Sopenharmony_ci int state; 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci genpd = container_of(genpd_dev, struct generic_pm_domain, dev); 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci if (unlikely(!genpd->opp_to_performance_state)) 305762306a36Sopenharmony_ci return 0; 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci genpd_lock(genpd); 306062306a36Sopenharmony_ci state = genpd->opp_to_performance_state(genpd, opp); 306162306a36Sopenharmony_ci genpd_unlock(genpd); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci return state; 306462306a36Sopenharmony_ci} 306562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_cistatic int __init genpd_bus_init(void) 306862306a36Sopenharmony_ci{ 306962306a36Sopenharmony_ci return bus_register(&genpd_bus_type); 307062306a36Sopenharmony_ci} 307162306a36Sopenharmony_cicore_initcall(genpd_bus_init); 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci/*** debugfs support ***/ 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 307962306a36Sopenharmony_ci/* 308062306a36Sopenharmony_ci * TODO: This function is a slightly modified version of rtpm_status_show 308162306a36Sopenharmony_ci * from sysfs.c, so generalize it. 308262306a36Sopenharmony_ci */ 308362306a36Sopenharmony_cistatic void rtpm_status_str(struct seq_file *s, struct device *dev) 308462306a36Sopenharmony_ci{ 308562306a36Sopenharmony_ci static const char * const status_lookup[] = { 308662306a36Sopenharmony_ci [RPM_ACTIVE] = "active", 308762306a36Sopenharmony_ci [RPM_RESUMING] = "resuming", 308862306a36Sopenharmony_ci [RPM_SUSPENDED] = "suspended", 308962306a36Sopenharmony_ci [RPM_SUSPENDING] = "suspending" 309062306a36Sopenharmony_ci }; 309162306a36Sopenharmony_ci const char *p = ""; 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci if (dev->power.runtime_error) 309462306a36Sopenharmony_ci p = "error"; 309562306a36Sopenharmony_ci else if (dev->power.disable_depth) 309662306a36Sopenharmony_ci p = "unsupported"; 309762306a36Sopenharmony_ci else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 309862306a36Sopenharmony_ci p = status_lookup[dev->power.runtime_status]; 309962306a36Sopenharmony_ci else 310062306a36Sopenharmony_ci WARN_ON(1); 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci seq_printf(s, "%-25s ", p); 310362306a36Sopenharmony_ci} 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_cistatic void perf_status_str(struct seq_file *s, struct device *dev) 310662306a36Sopenharmony_ci{ 310762306a36Sopenharmony_ci struct generic_pm_domain_data *gpd_data; 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 311062306a36Sopenharmony_ci seq_put_decimal_ull(s, "", gpd_data->performance_state); 311162306a36Sopenharmony_ci} 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_cistatic int genpd_summary_one(struct seq_file *s, 311462306a36Sopenharmony_ci struct generic_pm_domain *genpd) 311562306a36Sopenharmony_ci{ 311662306a36Sopenharmony_ci static const char * const status_lookup[] = { 311762306a36Sopenharmony_ci [GENPD_STATE_ON] = "on", 311862306a36Sopenharmony_ci [GENPD_STATE_OFF] = "off" 311962306a36Sopenharmony_ci }; 312062306a36Sopenharmony_ci struct pm_domain_data *pm_data; 312162306a36Sopenharmony_ci const char *kobj_path; 312262306a36Sopenharmony_ci struct gpd_link *link; 312362306a36Sopenharmony_ci char state[16]; 312462306a36Sopenharmony_ci int ret; 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 312762306a36Sopenharmony_ci if (ret) 312862306a36Sopenharmony_ci return -ERESTARTSYS; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 313162306a36Sopenharmony_ci goto exit; 313262306a36Sopenharmony_ci if (!genpd_status_on(genpd)) 313362306a36Sopenharmony_ci snprintf(state, sizeof(state), "%s-%u", 313462306a36Sopenharmony_ci status_lookup[genpd->status], genpd->state_idx); 313562306a36Sopenharmony_ci else 313662306a36Sopenharmony_ci snprintf(state, sizeof(state), "%s", 313762306a36Sopenharmony_ci status_lookup[genpd->status]); 313862306a36Sopenharmony_ci seq_printf(s, "%-30s %-50s %u", genpd->name, state, genpd->performance_state); 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci /* 314162306a36Sopenharmony_ci * Modifications on the list require holding locks on both 314262306a36Sopenharmony_ci * parent and child, so we are safe. 314362306a36Sopenharmony_ci * Also genpd->name is immutable. 314462306a36Sopenharmony_ci */ 314562306a36Sopenharmony_ci list_for_each_entry(link, &genpd->parent_links, parent_node) { 314662306a36Sopenharmony_ci if (list_is_first(&link->parent_node, &genpd->parent_links)) 314762306a36Sopenharmony_ci seq_printf(s, "\n%48s", " "); 314862306a36Sopenharmony_ci seq_printf(s, "%s", link->child->name); 314962306a36Sopenharmony_ci if (!list_is_last(&link->parent_node, &genpd->parent_links)) 315062306a36Sopenharmony_ci seq_puts(s, ", "); 315162306a36Sopenharmony_ci } 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 315462306a36Sopenharmony_ci kobj_path = kobject_get_path(&pm_data->dev->kobj, 315562306a36Sopenharmony_ci genpd_is_irq_safe(genpd) ? 315662306a36Sopenharmony_ci GFP_ATOMIC : GFP_KERNEL); 315762306a36Sopenharmony_ci if (kobj_path == NULL) 315862306a36Sopenharmony_ci continue; 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci seq_printf(s, "\n %-50s ", kobj_path); 316162306a36Sopenharmony_ci rtpm_status_str(s, pm_data->dev); 316262306a36Sopenharmony_ci perf_status_str(s, pm_data->dev); 316362306a36Sopenharmony_ci kfree(kobj_path); 316462306a36Sopenharmony_ci } 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_ci seq_puts(s, "\n"); 316762306a36Sopenharmony_ciexit: 316862306a36Sopenharmony_ci genpd_unlock(genpd); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci return 0; 317162306a36Sopenharmony_ci} 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_cistatic int summary_show(struct seq_file *s, void *data) 317462306a36Sopenharmony_ci{ 317562306a36Sopenharmony_ci struct generic_pm_domain *genpd; 317662306a36Sopenharmony_ci int ret = 0; 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci seq_puts(s, "domain status children performance\n"); 317962306a36Sopenharmony_ci seq_puts(s, " /device runtime status\n"); 318062306a36Sopenharmony_ci seq_puts(s, "----------------------------------------------------------------------------------------------\n"); 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci ret = mutex_lock_interruptible(&gpd_list_lock); 318362306a36Sopenharmony_ci if (ret) 318462306a36Sopenharmony_ci return -ERESTARTSYS; 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 318762306a36Sopenharmony_ci ret = genpd_summary_one(s, genpd); 318862306a36Sopenharmony_ci if (ret) 318962306a36Sopenharmony_ci break; 319062306a36Sopenharmony_ci } 319162306a36Sopenharmony_ci mutex_unlock(&gpd_list_lock); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci return ret; 319462306a36Sopenharmony_ci} 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_cistatic int status_show(struct seq_file *s, void *data) 319762306a36Sopenharmony_ci{ 319862306a36Sopenharmony_ci static const char * const status_lookup[] = { 319962306a36Sopenharmony_ci [GENPD_STATE_ON] = "on", 320062306a36Sopenharmony_ci [GENPD_STATE_OFF] = "off" 320162306a36Sopenharmony_ci }; 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 320462306a36Sopenharmony_ci int ret = 0; 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 320762306a36Sopenharmony_ci if (ret) 320862306a36Sopenharmony_ci return -ERESTARTSYS; 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup))) 321162306a36Sopenharmony_ci goto exit; 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_ci if (genpd->status == GENPD_STATE_OFF) 321462306a36Sopenharmony_ci seq_printf(s, "%s-%u\n", status_lookup[genpd->status], 321562306a36Sopenharmony_ci genpd->state_idx); 321662306a36Sopenharmony_ci else 321762306a36Sopenharmony_ci seq_printf(s, "%s\n", status_lookup[genpd->status]); 321862306a36Sopenharmony_ciexit: 321962306a36Sopenharmony_ci genpd_unlock(genpd); 322062306a36Sopenharmony_ci return ret; 322162306a36Sopenharmony_ci} 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_cistatic int sub_domains_show(struct seq_file *s, void *data) 322462306a36Sopenharmony_ci{ 322562306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 322662306a36Sopenharmony_ci struct gpd_link *link; 322762306a36Sopenharmony_ci int ret = 0; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 323062306a36Sopenharmony_ci if (ret) 323162306a36Sopenharmony_ci return -ERESTARTSYS; 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci list_for_each_entry(link, &genpd->parent_links, parent_node) 323462306a36Sopenharmony_ci seq_printf(s, "%s\n", link->child->name); 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci genpd_unlock(genpd); 323762306a36Sopenharmony_ci return ret; 323862306a36Sopenharmony_ci} 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_cistatic int idle_states_show(struct seq_file *s, void *data) 324162306a36Sopenharmony_ci{ 324262306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 324362306a36Sopenharmony_ci u64 now, delta, idle_time = 0; 324462306a36Sopenharmony_ci unsigned int i; 324562306a36Sopenharmony_ci int ret = 0; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 324862306a36Sopenharmony_ci if (ret) 324962306a36Sopenharmony_ci return -ERESTARTSYS; 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci seq_puts(s, "State Time Spent(ms) Usage Rejected\n"); 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci for (i = 0; i < genpd->state_count; i++) { 325462306a36Sopenharmony_ci idle_time += genpd->states[i].idle_time; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) { 325762306a36Sopenharmony_ci now = ktime_get_mono_fast_ns(); 325862306a36Sopenharmony_ci if (now > genpd->accounting_time) { 325962306a36Sopenharmony_ci delta = now - genpd->accounting_time; 326062306a36Sopenharmony_ci idle_time += delta; 326162306a36Sopenharmony_ci } 326262306a36Sopenharmony_ci } 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci do_div(idle_time, NSEC_PER_MSEC); 326562306a36Sopenharmony_ci seq_printf(s, "S%-13i %-14llu %-14llu %llu\n", i, idle_time, 326662306a36Sopenharmony_ci genpd->states[i].usage, genpd->states[i].rejected); 326762306a36Sopenharmony_ci } 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci genpd_unlock(genpd); 327062306a36Sopenharmony_ci return ret; 327162306a36Sopenharmony_ci} 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_cistatic int active_time_show(struct seq_file *s, void *data) 327462306a36Sopenharmony_ci{ 327562306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 327662306a36Sopenharmony_ci u64 now, on_time, delta = 0; 327762306a36Sopenharmony_ci int ret = 0; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 328062306a36Sopenharmony_ci if (ret) 328162306a36Sopenharmony_ci return -ERESTARTSYS; 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci if (genpd->status == GENPD_STATE_ON) { 328462306a36Sopenharmony_ci now = ktime_get_mono_fast_ns(); 328562306a36Sopenharmony_ci if (now > genpd->accounting_time) 328662306a36Sopenharmony_ci delta = now - genpd->accounting_time; 328762306a36Sopenharmony_ci } 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_ci on_time = genpd->on_time + delta; 329062306a36Sopenharmony_ci do_div(on_time, NSEC_PER_MSEC); 329162306a36Sopenharmony_ci seq_printf(s, "%llu ms\n", on_time); 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci genpd_unlock(genpd); 329462306a36Sopenharmony_ci return ret; 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_cistatic int total_idle_time_show(struct seq_file *s, void *data) 329862306a36Sopenharmony_ci{ 329962306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 330062306a36Sopenharmony_ci u64 now, delta, total = 0; 330162306a36Sopenharmony_ci unsigned int i; 330262306a36Sopenharmony_ci int ret = 0; 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 330562306a36Sopenharmony_ci if (ret) 330662306a36Sopenharmony_ci return -ERESTARTSYS; 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci for (i = 0; i < genpd->state_count; i++) { 330962306a36Sopenharmony_ci total += genpd->states[i].idle_time; 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) { 331262306a36Sopenharmony_ci now = ktime_get_mono_fast_ns(); 331362306a36Sopenharmony_ci if (now > genpd->accounting_time) { 331462306a36Sopenharmony_ci delta = now - genpd->accounting_time; 331562306a36Sopenharmony_ci total += delta; 331662306a36Sopenharmony_ci } 331762306a36Sopenharmony_ci } 331862306a36Sopenharmony_ci } 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci do_div(total, NSEC_PER_MSEC); 332162306a36Sopenharmony_ci seq_printf(s, "%llu ms\n", total); 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci genpd_unlock(genpd); 332462306a36Sopenharmony_ci return ret; 332562306a36Sopenharmony_ci} 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_cistatic int devices_show(struct seq_file *s, void *data) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 333162306a36Sopenharmony_ci struct pm_domain_data *pm_data; 333262306a36Sopenharmony_ci const char *kobj_path; 333362306a36Sopenharmony_ci int ret = 0; 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci ret = genpd_lock_interruptible(genpd); 333662306a36Sopenharmony_ci if (ret) 333762306a36Sopenharmony_ci return -ERESTARTSYS; 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 334062306a36Sopenharmony_ci kobj_path = kobject_get_path(&pm_data->dev->kobj, 334162306a36Sopenharmony_ci genpd_is_irq_safe(genpd) ? 334262306a36Sopenharmony_ci GFP_ATOMIC : GFP_KERNEL); 334362306a36Sopenharmony_ci if (kobj_path == NULL) 334462306a36Sopenharmony_ci continue; 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci seq_printf(s, "%s\n", kobj_path); 334762306a36Sopenharmony_ci kfree(kobj_path); 334862306a36Sopenharmony_ci } 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci genpd_unlock(genpd); 335162306a36Sopenharmony_ci return ret; 335262306a36Sopenharmony_ci} 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_cistatic int perf_state_show(struct seq_file *s, void *data) 335562306a36Sopenharmony_ci{ 335662306a36Sopenharmony_ci struct generic_pm_domain *genpd = s->private; 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci if (genpd_lock_interruptible(genpd)) 335962306a36Sopenharmony_ci return -ERESTARTSYS; 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci seq_printf(s, "%u\n", genpd->performance_state); 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci genpd_unlock(genpd); 336462306a36Sopenharmony_ci return 0; 336562306a36Sopenharmony_ci} 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(summary); 336862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(status); 336962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(sub_domains); 337062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(idle_states); 337162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(active_time); 337262306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(total_idle_time); 337362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(devices); 337462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(perf_state); 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_cistatic void genpd_debug_add(struct generic_pm_domain *genpd) 337762306a36Sopenharmony_ci{ 337862306a36Sopenharmony_ci struct dentry *d; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci if (!genpd_debugfs_dir) 338162306a36Sopenharmony_ci return; 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_ci d = debugfs_create_dir(genpd->name, genpd_debugfs_dir); 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci debugfs_create_file("current_state", 0444, 338662306a36Sopenharmony_ci d, genpd, &status_fops); 338762306a36Sopenharmony_ci debugfs_create_file("sub_domains", 0444, 338862306a36Sopenharmony_ci d, genpd, &sub_domains_fops); 338962306a36Sopenharmony_ci debugfs_create_file("idle_states", 0444, 339062306a36Sopenharmony_ci d, genpd, &idle_states_fops); 339162306a36Sopenharmony_ci debugfs_create_file("active_time", 0444, 339262306a36Sopenharmony_ci d, genpd, &active_time_fops); 339362306a36Sopenharmony_ci debugfs_create_file("total_idle_time", 0444, 339462306a36Sopenharmony_ci d, genpd, &total_idle_time_fops); 339562306a36Sopenharmony_ci debugfs_create_file("devices", 0444, 339662306a36Sopenharmony_ci d, genpd, &devices_fops); 339762306a36Sopenharmony_ci if (genpd->set_performance_state) 339862306a36Sopenharmony_ci debugfs_create_file("perf_state", 0444, 339962306a36Sopenharmony_ci d, genpd, &perf_state_fops); 340062306a36Sopenharmony_ci} 340162306a36Sopenharmony_ci 340262306a36Sopenharmony_cistatic int __init genpd_debug_init(void) 340362306a36Sopenharmony_ci{ 340462306a36Sopenharmony_ci struct generic_pm_domain *genpd; 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 340762306a36Sopenharmony_ci 340862306a36Sopenharmony_ci debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir, 340962306a36Sopenharmony_ci NULL, &summary_fops); 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci list_for_each_entry(genpd, &gpd_list, gpd_list_node) 341262306a36Sopenharmony_ci genpd_debug_add(genpd); 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci return 0; 341562306a36Sopenharmony_ci} 341662306a36Sopenharmony_cilate_initcall(genpd_debug_init); 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_cistatic void __exit genpd_debug_exit(void) 341962306a36Sopenharmony_ci{ 342062306a36Sopenharmony_ci debugfs_remove_recursive(genpd_debugfs_dir); 342162306a36Sopenharmony_ci} 342262306a36Sopenharmony_ci__exitcall(genpd_debug_exit); 342362306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 3424