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