xref: /openbmc/linux/drivers/base/power/domain.c (revision d97fe100)
15de363b6SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2f721889fSRafael J. Wysocki /*
3f721889fSRafael J. Wysocki  * drivers/base/power/domain.c - Common code related to device power domains.
4f721889fSRafael J. Wysocki  *
5f721889fSRafael J. Wysocki  * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
6f721889fSRafael J. Wysocki  */
77a5bd127SJoe Perches #define pr_fmt(fmt) "PM: " fmt
87a5bd127SJoe Perches 
993af5e93SGeert Uytterhoeven #include <linux/delay.h>
10f721889fSRafael J. Wysocki #include <linux/kernel.h>
11f721889fSRafael J. Wysocki #include <linux/io.h>
12aa42240aSTomasz Figa #include <linux/platform_device.h>
136a0ae73dSViresh Kumar #include <linux/pm_opp.h>
14f721889fSRafael J. Wysocki #include <linux/pm_runtime.h>
15f721889fSRafael J. Wysocki #include <linux/pm_domain.h>
166ff7bb0dSRafael J. Wysocki #include <linux/pm_qos.h>
17c11f6f5bSUlf Hansson #include <linux/pm_clock.h>
18f721889fSRafael J. Wysocki #include <linux/slab.h>
19f721889fSRafael J. Wysocki #include <linux/err.h>
2017b75ecaSRafael J. Wysocki #include <linux/sched.h>
2117b75ecaSRafael J. Wysocki #include <linux/suspend.h>
22d5e4cbfeSRafael J. Wysocki #include <linux/export.h>
23eb594b73SUlf Hansson #include <linux/cpu.h>
24718072ceSThierry Strudel #include <linux/debugfs.h>
25d5e4cbfeSRafael J. Wysocki 
26aa8e54b5STomeu Vizoso #include "power.h"
27aa8e54b5STomeu Vizoso 
2893af5e93SGeert Uytterhoeven #define GENPD_RETRY_MAX_MS	250		/* Approximate */
2993af5e93SGeert Uytterhoeven 
30d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev)		\
31d5e4cbfeSRafael J. Wysocki ({								\
32d5e4cbfeSRafael J. Wysocki 	type (*__routine)(struct device *__d); 			\
33d5e4cbfeSRafael J. Wysocki 	type __ret = (type)0;					\
34d5e4cbfeSRafael J. Wysocki 								\
35d5e4cbfeSRafael J. Wysocki 	__routine = genpd->dev_ops.callback; 			\
36d5e4cbfeSRafael J. Wysocki 	if (__routine) {					\
37d5e4cbfeSRafael J. Wysocki 		__ret = __routine(dev); 			\
38d5e4cbfeSRafael J. Wysocki 	}							\
39d5e4cbfeSRafael J. Wysocki 	__ret;							\
40d5e4cbfeSRafael J. Wysocki })
41f721889fSRafael J. Wysocki 
425125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list);
435125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock);
445125bbf3SRafael J. Wysocki 
4535241d12SLina Iyer struct genpd_lock_ops {
4635241d12SLina Iyer 	void (*lock)(struct generic_pm_domain *genpd);
4735241d12SLina Iyer 	void (*lock_nested)(struct generic_pm_domain *genpd, int depth);
4835241d12SLina Iyer 	int (*lock_interruptible)(struct generic_pm_domain *genpd);
4935241d12SLina Iyer 	void (*unlock)(struct generic_pm_domain *genpd);
5035241d12SLina Iyer };
5135241d12SLina Iyer 
5235241d12SLina Iyer static void genpd_lock_mtx(struct generic_pm_domain *genpd)
5335241d12SLina Iyer {
5435241d12SLina Iyer 	mutex_lock(&genpd->mlock);
5535241d12SLina Iyer }
5635241d12SLina Iyer 
5735241d12SLina Iyer static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd,
5835241d12SLina Iyer 					int depth)
5935241d12SLina Iyer {
6035241d12SLina Iyer 	mutex_lock_nested(&genpd->mlock, depth);
6135241d12SLina Iyer }
6235241d12SLina Iyer 
6335241d12SLina Iyer static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd)
6435241d12SLina Iyer {
6535241d12SLina Iyer 	return mutex_lock_interruptible(&genpd->mlock);
6635241d12SLina Iyer }
6735241d12SLina Iyer 
6835241d12SLina Iyer static void genpd_unlock_mtx(struct generic_pm_domain *genpd)
6935241d12SLina Iyer {
7035241d12SLina Iyer 	return mutex_unlock(&genpd->mlock);
7135241d12SLina Iyer }
7235241d12SLina Iyer 
7335241d12SLina Iyer static const struct genpd_lock_ops genpd_mtx_ops = {
7435241d12SLina Iyer 	.lock = genpd_lock_mtx,
7535241d12SLina Iyer 	.lock_nested = genpd_lock_nested_mtx,
7635241d12SLina Iyer 	.lock_interruptible = genpd_lock_interruptible_mtx,
7735241d12SLina Iyer 	.unlock = genpd_unlock_mtx,
7835241d12SLina Iyer };
7935241d12SLina Iyer 
80d716f479SLina Iyer static void genpd_lock_spin(struct generic_pm_domain *genpd)
81d716f479SLina Iyer 	__acquires(&genpd->slock)
82d716f479SLina Iyer {
83d716f479SLina Iyer 	unsigned long flags;
84d716f479SLina Iyer 
85d716f479SLina Iyer 	spin_lock_irqsave(&genpd->slock, flags);
86d716f479SLina Iyer 	genpd->lock_flags = flags;
87d716f479SLina Iyer }
88d716f479SLina Iyer 
89d716f479SLina Iyer static void genpd_lock_nested_spin(struct generic_pm_domain *genpd,
90d716f479SLina Iyer 					int depth)
91d716f479SLina Iyer 	__acquires(&genpd->slock)
92d716f479SLina Iyer {
93d716f479SLina Iyer 	unsigned long flags;
94d716f479SLina Iyer 
95d716f479SLina Iyer 	spin_lock_irqsave_nested(&genpd->slock, flags, depth);
96d716f479SLina Iyer 	genpd->lock_flags = flags;
97d716f479SLina Iyer }
98d716f479SLina Iyer 
99d716f479SLina Iyer static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd)
100d716f479SLina Iyer 	__acquires(&genpd->slock)
101d716f479SLina Iyer {
102d716f479SLina Iyer 	unsigned long flags;
103d716f479SLina Iyer 
104d716f479SLina Iyer 	spin_lock_irqsave(&genpd->slock, flags);
105d716f479SLina Iyer 	genpd->lock_flags = flags;
106d716f479SLina Iyer 	return 0;
107d716f479SLina Iyer }
108d716f479SLina Iyer 
109d716f479SLina Iyer static void genpd_unlock_spin(struct generic_pm_domain *genpd)
110d716f479SLina Iyer 	__releases(&genpd->slock)
111d716f479SLina Iyer {
112d716f479SLina Iyer 	spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags);
113d716f479SLina Iyer }
114d716f479SLina Iyer 
115d716f479SLina Iyer static const struct genpd_lock_ops genpd_spin_ops = {
116d716f479SLina Iyer 	.lock = genpd_lock_spin,
117d716f479SLina Iyer 	.lock_nested = genpd_lock_nested_spin,
118d716f479SLina Iyer 	.lock_interruptible = genpd_lock_interruptible_spin,
119d716f479SLina Iyer 	.unlock = genpd_unlock_spin,
120d716f479SLina Iyer };
121d716f479SLina Iyer 
12235241d12SLina Iyer #define genpd_lock(p)			p->lock_ops->lock(p)
12335241d12SLina Iyer #define genpd_lock_nested(p, d)		p->lock_ops->lock_nested(p, d)
12435241d12SLina Iyer #define genpd_lock_interruptible(p)	p->lock_ops->lock_interruptible(p)
12535241d12SLina Iyer #define genpd_unlock(p)			p->lock_ops->unlock(p)
12635241d12SLina Iyer 
12749f618e1SUlf Hansson #define genpd_status_on(genpd)		(genpd->status == GENPD_STATE_ON)
128d716f479SLina Iyer #define genpd_is_irq_safe(genpd)	(genpd->flags & GENPD_FLAG_IRQ_SAFE)
129ffaa42e8SUlf Hansson #define genpd_is_always_on(genpd)	(genpd->flags & GENPD_FLAG_ALWAYS_ON)
13095a20ef6SGeert Uytterhoeven #define genpd_is_active_wakeup(genpd)	(genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
131eb594b73SUlf Hansson #define genpd_is_cpu_domain(genpd)	(genpd->flags & GENPD_FLAG_CPU_DOMAIN)
132ed61e18aSLeonard Crestez #define genpd_is_rpm_always_on(genpd)	(genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
133d716f479SLina Iyer 
134d716f479SLina Iyer static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
135d8600c8bSKrzysztof Kozlowski 		const struct generic_pm_domain *genpd)
136d716f479SLina Iyer {
137d716f479SLina Iyer 	bool ret;
138d716f479SLina Iyer 
139d716f479SLina Iyer 	ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd);
140d716f479SLina Iyer 
141075c37d5SUlf Hansson 	/*
142075c37d5SUlf Hansson 	 * Warn once if an IRQ safe device is attached to a no sleep domain, as
143075c37d5SUlf Hansson 	 * to indicate a suboptimal configuration for PM. For an always on
144075c37d5SUlf Hansson 	 * domain this isn't case, thus don't warn.
145075c37d5SUlf Hansson 	 */
146075c37d5SUlf Hansson 	if (ret && !genpd_is_always_on(genpd))
147d716f479SLina Iyer 		dev_warn_once(dev, "PM domain %s will not be powered off\n",
148d716f479SLina Iyer 				genpd->name);
149d716f479SLina Iyer 
150d716f479SLina Iyer 	return ret;
151d716f479SLina Iyer }
152d716f479SLina Iyer 
153b3ad17c0SUlf Hansson static int genpd_runtime_suspend(struct device *dev);
154b3ad17c0SUlf Hansson 
155446d999cSRussell King /*
156446d999cSRussell King  * Get the generic PM domain for a particular struct device.
157446d999cSRussell King  * This validates the struct device pointer, the PM domain pointer,
158446d999cSRussell King  * and checks that the PM domain pointer is a real generic PM domain.
159446d999cSRussell King  * Any failure results in NULL being returned.
160446d999cSRussell King  */
161b3ad17c0SUlf Hansson static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev)
162446d999cSRussell King {
163446d999cSRussell King 	if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain))
164446d999cSRussell King 		return NULL;
165446d999cSRussell King 
166b3ad17c0SUlf Hansson 	/* A genpd's always have its ->runtime_suspend() callback assigned. */
167b3ad17c0SUlf Hansson 	if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend)
168b3ad17c0SUlf Hansson 		return pd_to_genpd(dev->pm_domain);
169446d999cSRussell King 
170b3ad17c0SUlf Hansson 	return NULL;
171446d999cSRussell King }
172446d999cSRussell King 
173446d999cSRussell King /*
174446d999cSRussell King  * This should only be used where we are certain that the pm_domain
175446d999cSRussell King  * attached to the device is a genpd domain.
176446d999cSRussell King  */
177446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev)
1785248051bSRafael J. Wysocki {
1795248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
1805248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
1815248051bSRafael J. Wysocki 
182596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
1835248051bSRafael J. Wysocki }
184f721889fSRafael J. Wysocki 
185d8600c8bSKrzysztof Kozlowski static int genpd_stop_dev(const struct generic_pm_domain *genpd,
186d8600c8bSKrzysztof Kozlowski 			  struct device *dev)
187d5e4cbfeSRafael J. Wysocki {
18851cda844SUlf Hansson 	return GENPD_DEV_CALLBACK(genpd, int, stop, dev);
189d5e4cbfeSRafael J. Wysocki }
190d5e4cbfeSRafael J. Wysocki 
191d8600c8bSKrzysztof Kozlowski static int genpd_start_dev(const struct generic_pm_domain *genpd,
192d8600c8bSKrzysztof Kozlowski 			   struct device *dev)
193d5e4cbfeSRafael J. Wysocki {
194ba2bbfbfSUlf Hansson 	return GENPD_DEV_CALLBACK(genpd, int, start, dev);
195d5e4cbfeSRafael J. Wysocki }
196d5e4cbfeSRafael J. Wysocki 
197c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
198f721889fSRafael J. Wysocki {
199c4bb3160SRafael J. Wysocki 	bool ret = false;
200c4bb3160SRafael J. Wysocki 
201c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
202c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
203c4bb3160SRafael J. Wysocki 
204c4bb3160SRafael J. Wysocki 	return ret;
205c4bb3160SRafael J. Wysocki }
206c4bb3160SRafael J. Wysocki 
207c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
208c4bb3160SRafael J. Wysocki {
209c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
2104e857c58SPeter Zijlstra 	smp_mb__after_atomic();
211f721889fSRafael J. Wysocki }
212f721889fSRafael J. Wysocki 
213afece3abSThara Gopinath #ifdef CONFIG_DEBUG_FS
214718072ceSThierry Strudel static struct dentry *genpd_debugfs_dir;
215718072ceSThierry Strudel 
216718072ceSThierry Strudel static void genpd_debug_add(struct generic_pm_domain *genpd);
217718072ceSThierry Strudel 
218718072ceSThierry Strudel static void genpd_debug_remove(struct generic_pm_domain *genpd)
219718072ceSThierry Strudel {
220718072ceSThierry Strudel 	struct dentry *d;
221718072ceSThierry Strudel 
222718072ceSThierry Strudel 	d = debugfs_lookup(genpd->name, genpd_debugfs_dir);
223718072ceSThierry Strudel 	debugfs_remove(d);
224718072ceSThierry Strudel }
225718072ceSThierry Strudel 
226afece3abSThara Gopinath static void genpd_update_accounting(struct generic_pm_domain *genpd)
227afece3abSThara Gopinath {
228afece3abSThara Gopinath 	ktime_t delta, now;
229afece3abSThara Gopinath 
230afece3abSThara Gopinath 	now = ktime_get();
231afece3abSThara Gopinath 	delta = ktime_sub(now, genpd->accounting_time);
232afece3abSThara Gopinath 
233afece3abSThara Gopinath 	/*
234afece3abSThara Gopinath 	 * If genpd->status is active, it means we are just
235afece3abSThara Gopinath 	 * out of off and so update the idle time and vice
236afece3abSThara Gopinath 	 * versa.
237afece3abSThara Gopinath 	 */
23849f618e1SUlf Hansson 	if (genpd->status == GENPD_STATE_ON) {
239afece3abSThara Gopinath 		int state_idx = genpd->state_idx;
240afece3abSThara Gopinath 
241afece3abSThara Gopinath 		genpd->states[state_idx].idle_time =
242afece3abSThara Gopinath 			ktime_add(genpd->states[state_idx].idle_time, delta);
243afece3abSThara Gopinath 	} else {
244afece3abSThara Gopinath 		genpd->on_time = ktime_add(genpd->on_time, delta);
245afece3abSThara Gopinath 	}
246afece3abSThara Gopinath 
247afece3abSThara Gopinath 	genpd->accounting_time = now;
248afece3abSThara Gopinath }
249afece3abSThara Gopinath #else
250718072ceSThierry Strudel static inline void genpd_debug_add(struct generic_pm_domain *genpd) {}
251718072ceSThierry Strudel static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}
252afece3abSThara Gopinath static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
253afece3abSThara Gopinath #endif
254afece3abSThara Gopinath 
255cd50c6d3SViresh Kumar static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
256cd50c6d3SViresh Kumar 					   unsigned int state)
257cd50c6d3SViresh Kumar {
258cd50c6d3SViresh Kumar 	struct generic_pm_domain_data *pd_data;
259cd50c6d3SViresh Kumar 	struct pm_domain_data *pdd;
26018edf49cSViresh Kumar 	struct gpd_link *link;
261cd50c6d3SViresh Kumar 
262cd50c6d3SViresh Kumar 	/* New requested state is same as Max requested state */
263cd50c6d3SViresh Kumar 	if (state == genpd->performance_state)
264cd50c6d3SViresh Kumar 		return state;
265cd50c6d3SViresh Kumar 
266cd50c6d3SViresh Kumar 	/* New requested state is higher than Max requested state */
267cd50c6d3SViresh Kumar 	if (state > genpd->performance_state)
268cd50c6d3SViresh Kumar 		return state;
269cd50c6d3SViresh Kumar 
270cd50c6d3SViresh Kumar 	/* Traverse all devices within the domain */
271cd50c6d3SViresh Kumar 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
272cd50c6d3SViresh Kumar 		pd_data = to_gpd_data(pdd);
273cd50c6d3SViresh Kumar 
274cd50c6d3SViresh Kumar 		if (pd_data->performance_state > state)
275cd50c6d3SViresh Kumar 			state = pd_data->performance_state;
276cd50c6d3SViresh Kumar 	}
277cd50c6d3SViresh Kumar 
278cd50c6d3SViresh Kumar 	/*
27918edf49cSViresh Kumar 	 * Traverse all sub-domains within the domain. This can be
28018edf49cSViresh Kumar 	 * done without any additional locking as the link->performance_state
2818d87ae48SKees Cook 	 * field is protected by the parent genpd->lock, which is already taken.
28218edf49cSViresh Kumar 	 *
28318edf49cSViresh Kumar 	 * Also note that link->performance_state (subdomain's performance state
2848d87ae48SKees Cook 	 * requirement to parent domain) is different from
2858d87ae48SKees Cook 	 * link->child->performance_state (current performance state requirement
28618edf49cSViresh Kumar 	 * of the devices/sub-domains of the subdomain) and so can have a
28718edf49cSViresh Kumar 	 * different value.
28818edf49cSViresh Kumar 	 *
28918edf49cSViresh Kumar 	 * Note that we also take vote from powered-off sub-domains into account
29018edf49cSViresh Kumar 	 * as the same is done for devices right now.
291cd50c6d3SViresh Kumar 	 */
2928d87ae48SKees Cook 	list_for_each_entry(link, &genpd->parent_links, parent_node) {
29318edf49cSViresh Kumar 		if (link->performance_state > state)
29418edf49cSViresh Kumar 			state = link->performance_state;
29518edf49cSViresh Kumar 	}
29618edf49cSViresh Kumar 
297cd50c6d3SViresh Kumar 	return state;
298cd50c6d3SViresh Kumar }
299cd50c6d3SViresh Kumar 
300079c42a0SDmitry Osipenko static int genpd_xlate_performance_state(struct generic_pm_domain *genpd,
301079c42a0SDmitry Osipenko 					 struct generic_pm_domain *parent,
302079c42a0SDmitry Osipenko 					 unsigned int pstate)
303079c42a0SDmitry Osipenko {
304079c42a0SDmitry Osipenko 	if (!parent->set_performance_state)
305079c42a0SDmitry Osipenko 		return pstate;
306079c42a0SDmitry Osipenko 
307079c42a0SDmitry Osipenko 	return dev_pm_opp_xlate_performance_state(genpd->opp_table,
308079c42a0SDmitry Osipenko 						  parent->opp_table,
309079c42a0SDmitry Osipenko 						  pstate);
310079c42a0SDmitry Osipenko }
311079c42a0SDmitry Osipenko 
312cd50c6d3SViresh Kumar static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
31318edf49cSViresh Kumar 					unsigned int state, int depth)
314cd50c6d3SViresh Kumar {
3158d87ae48SKees Cook 	struct generic_pm_domain *parent;
31618edf49cSViresh Kumar 	struct gpd_link *link;
3178d87ae48SKees Cook 	int parent_state, ret;
318cd50c6d3SViresh Kumar 
319cd50c6d3SViresh Kumar 	if (state == genpd->performance_state)
320cd50c6d3SViresh Kumar 		return 0;
321cd50c6d3SViresh Kumar 
3228d87ae48SKees Cook 	/* Propagate to parents of genpd */
3238d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
3248d87ae48SKees Cook 		parent = link->parent;
32518edf49cSViresh Kumar 
3268d87ae48SKees Cook 		/* Find parent's performance state */
327079c42a0SDmitry Osipenko 		ret = genpd_xlate_performance_state(genpd, parent, state);
32818edf49cSViresh Kumar 		if (unlikely(ret < 0))
32918edf49cSViresh Kumar 			goto err;
33018edf49cSViresh Kumar 
3318d87ae48SKees Cook 		parent_state = ret;
33218edf49cSViresh Kumar 
3338d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
33418edf49cSViresh Kumar 
33518edf49cSViresh Kumar 		link->prev_performance_state = link->performance_state;
3368d87ae48SKees Cook 		link->performance_state = parent_state;
3378d87ae48SKees Cook 		parent_state = _genpd_reeval_performance_state(parent,
3388d87ae48SKees Cook 						parent_state);
3398d87ae48SKees Cook 		ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
34018edf49cSViresh Kumar 		if (ret)
34118edf49cSViresh Kumar 			link->performance_state = link->prev_performance_state;
34218edf49cSViresh Kumar 
3438d87ae48SKees Cook 		genpd_unlock(parent);
34418edf49cSViresh Kumar 
34518edf49cSViresh Kumar 		if (ret)
34618edf49cSViresh Kumar 			goto err;
34718edf49cSViresh Kumar 	}
34818edf49cSViresh Kumar 
349079c42a0SDmitry Osipenko 	if (genpd->set_performance_state) {
350cd50c6d3SViresh Kumar 		ret = genpd->set_performance_state(genpd, state);
351cd50c6d3SViresh Kumar 		if (ret)
35218edf49cSViresh Kumar 			goto err;
353079c42a0SDmitry Osipenko 	}
354cd50c6d3SViresh Kumar 
355cd50c6d3SViresh Kumar 	genpd->performance_state = state;
356cd50c6d3SViresh Kumar 	return 0;
35718edf49cSViresh Kumar 
35818edf49cSViresh Kumar err:
35918edf49cSViresh Kumar 	/* Encountered an error, lets rollback */
3608d87ae48SKees Cook 	list_for_each_entry_continue_reverse(link, &genpd->child_links,
3618d87ae48SKees Cook 					     child_node) {
3628d87ae48SKees Cook 		parent = link->parent;
36318edf49cSViresh Kumar 
3648d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
36518edf49cSViresh Kumar 
3668d87ae48SKees Cook 		parent_state = link->prev_performance_state;
3678d87ae48SKees Cook 		link->performance_state = parent_state;
36818edf49cSViresh Kumar 
3698d87ae48SKees Cook 		parent_state = _genpd_reeval_performance_state(parent,
3708d87ae48SKees Cook 						parent_state);
3718d87ae48SKees Cook 		if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
37218edf49cSViresh Kumar 			pr_err("%s: Failed to roll back to %d performance state\n",
3738d87ae48SKees Cook 			       parent->name, parent_state);
37418edf49cSViresh Kumar 		}
37518edf49cSViresh Kumar 
3768d87ae48SKees Cook 		genpd_unlock(parent);
37718edf49cSViresh Kumar 	}
37818edf49cSViresh Kumar 
37918edf49cSViresh Kumar 	return ret;
380cd50c6d3SViresh Kumar }
381cd50c6d3SViresh Kumar 
3820eef091dSUlf Hansson static int genpd_set_performance_state(struct device *dev, unsigned int state)
3830eef091dSUlf Hansson {
3840eef091dSUlf Hansson 	struct generic_pm_domain *genpd = dev_to_genpd(dev);
3850eef091dSUlf Hansson 	struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
3860eef091dSUlf Hansson 	unsigned int prev_state;
3870eef091dSUlf Hansson 	int ret;
3880eef091dSUlf Hansson 
3890eef091dSUlf Hansson 	prev_state = gpd_data->performance_state;
390*d97fe100SUlf Hansson 	if (prev_state == state)
391*d97fe100SUlf Hansson 		return 0;
392*d97fe100SUlf Hansson 
3930eef091dSUlf Hansson 	gpd_data->performance_state = state;
3940eef091dSUlf Hansson 	state = _genpd_reeval_performance_state(genpd, state);
3950eef091dSUlf Hansson 
3960eef091dSUlf Hansson 	ret = _genpd_set_performance_state(genpd, state, 0);
3970eef091dSUlf Hansson 	if (ret)
3980eef091dSUlf Hansson 		gpd_data->performance_state = prev_state;
3990eef091dSUlf Hansson 
4000eef091dSUlf Hansson 	return ret;
4010eef091dSUlf Hansson }
4020eef091dSUlf Hansson 
40342f6284aSViresh Kumar /**
40442f6284aSViresh Kumar  * dev_pm_genpd_set_performance_state- Set performance state of device's power
40542f6284aSViresh Kumar  * domain.
40642f6284aSViresh Kumar  *
40742f6284aSViresh Kumar  * @dev: Device for which the performance-state needs to be set.
40842f6284aSViresh Kumar  * @state: Target performance state of the device. This can be set as 0 when the
40942f6284aSViresh Kumar  *	   device doesn't have any performance state constraints left (And so
41042f6284aSViresh Kumar  *	   the device wouldn't participate anymore to find the target
41142f6284aSViresh Kumar  *	   performance state of the genpd).
41242f6284aSViresh Kumar  *
41342f6284aSViresh Kumar  * It is assumed that the users guarantee that the genpd wouldn't be detached
41442f6284aSViresh Kumar  * while this routine is getting called.
41542f6284aSViresh Kumar  *
41642f6284aSViresh Kumar  * Returns 0 on success and negative error values on failures.
41742f6284aSViresh Kumar  */
41842f6284aSViresh Kumar int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
41942f6284aSViresh Kumar {
42042f6284aSViresh Kumar 	struct generic_pm_domain *genpd;
421cd50c6d3SViresh Kumar 	int ret;
42242f6284aSViresh Kumar 
4233ea4ca92SUlf Hansson 	genpd = dev_to_genpd_safe(dev);
4243ea4ca92SUlf Hansson 	if (!genpd)
42542f6284aSViresh Kumar 		return -ENODEV;
42642f6284aSViresh Kumar 
427e757e7faSYangtao Li 	if (WARN_ON(!dev->power.subsys_data ||
428e757e7faSYangtao Li 		     !dev->power.subsys_data->domain_data))
42942f6284aSViresh Kumar 		return -EINVAL;
43042f6284aSViresh Kumar 
43142f6284aSViresh Kumar 	genpd_lock(genpd);
4320eef091dSUlf Hansson 	ret = genpd_set_performance_state(dev, state);
43342f6284aSViresh Kumar 	genpd_unlock(genpd);
43442f6284aSViresh Kumar 
43542f6284aSViresh Kumar 	return ret;
43642f6284aSViresh Kumar }
43742f6284aSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state);
43842f6284aSViresh Kumar 
43967e3242eSLina Iyer /**
44067e3242eSLina Iyer  * dev_pm_genpd_set_next_wakeup - Notify PM framework of an impending wakeup.
44167e3242eSLina Iyer  *
44267e3242eSLina Iyer  * @dev: Device to handle
44367e3242eSLina Iyer  * @next: impending interrupt/wakeup for the device
44467e3242eSLina Iyer  *
44567e3242eSLina Iyer  *
44667e3242eSLina Iyer  * Allow devices to inform of the next wakeup. It's assumed that the users
44767e3242eSLina Iyer  * guarantee that the genpd wouldn't be detached while this routine is getting
44867e3242eSLina Iyer  * called. Additionally, it's also assumed that @dev isn't runtime suspended
44967e3242eSLina Iyer  * (RPM_SUSPENDED)."
45067e3242eSLina Iyer  * Although devices are expected to update the next_wakeup after the end of
45167e3242eSLina Iyer  * their usecase as well, it is possible the devices themselves may not know
45267e3242eSLina Iyer  * about that, so stale @next will be ignored when powering off the domain.
45367e3242eSLina Iyer  */
45467e3242eSLina Iyer void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next)
45567e3242eSLina Iyer {
45667e3242eSLina Iyer 	struct generic_pm_domain_data *gpd_data;
45767e3242eSLina Iyer 	struct generic_pm_domain *genpd;
45867e3242eSLina Iyer 
45967e3242eSLina Iyer 	genpd = dev_to_genpd_safe(dev);
46067e3242eSLina Iyer 	if (!genpd)
46167e3242eSLina Iyer 		return;
46267e3242eSLina Iyer 
46367e3242eSLina Iyer 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
46467e3242eSLina Iyer 	gpd_data->next_wakeup = next;
46567e3242eSLina Iyer }
46667e3242eSLina Iyer EXPORT_SYMBOL_GPL(dev_pm_genpd_set_next_wakeup);
46767e3242eSLina Iyer 
46886e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
469c8f0ea45SGeert Uytterhoeven {
470fc5cbf0cSAxel Haslam 	unsigned int state_idx = genpd->state_idx;
471c8f0ea45SGeert Uytterhoeven 	ktime_t time_start;
472c8f0ea45SGeert Uytterhoeven 	s64 elapsed_ns;
473330e3932SUlf Hansson 	int ret;
474d4f81383SUlf Hansson 
475d4f81383SUlf Hansson 	/* Notify consumers that we are about to power on. */
476330e3932SUlf Hansson 	ret = raw_notifier_call_chain_robust(&genpd->power_notifiers,
477330e3932SUlf Hansson 					     GENPD_NOTIFY_PRE_ON,
478330e3932SUlf Hansson 					     GENPD_NOTIFY_OFF, NULL);
479d4f81383SUlf Hansson 	ret = notifier_to_errno(ret);
480d4f81383SUlf Hansson 	if (ret)
481330e3932SUlf Hansson 		return ret;
482c8f0ea45SGeert Uytterhoeven 
483c8f0ea45SGeert Uytterhoeven 	if (!genpd->power_on)
484d4f81383SUlf Hansson 		goto out;
485c8f0ea45SGeert Uytterhoeven 
486d4f81383SUlf Hansson 	if (!timed) {
487d4f81383SUlf Hansson 		ret = genpd->power_on(genpd);
488d4f81383SUlf Hansson 		if (ret)
489d4f81383SUlf Hansson 			goto err;
490d4f81383SUlf Hansson 
491d4f81383SUlf Hansson 		goto out;
492d4f81383SUlf Hansson 	}
493a4630c61SGeert Uytterhoeven 
494c8f0ea45SGeert Uytterhoeven 	time_start = ktime_get();
495c8f0ea45SGeert Uytterhoeven 	ret = genpd->power_on(genpd);
496c8f0ea45SGeert Uytterhoeven 	if (ret)
497d4f81383SUlf Hansson 		goto err;
498c8f0ea45SGeert Uytterhoeven 
499c8f0ea45SGeert Uytterhoeven 	elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
500fc5cbf0cSAxel Haslam 	if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
501d4f81383SUlf Hansson 		goto out;
502c8f0ea45SGeert Uytterhoeven 
503fc5cbf0cSAxel Haslam 	genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
504c8f0ea45SGeert Uytterhoeven 	genpd->max_off_time_changed = true;
5056d7d5c32SRussell King 	pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
506c8f0ea45SGeert Uytterhoeven 		 genpd->name, "on", elapsed_ns);
507c8f0ea45SGeert Uytterhoeven 
508d4f81383SUlf Hansson out:
509d4f81383SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
510d4f81383SUlf Hansson 	return 0;
511d4f81383SUlf Hansson err:
512d4f81383SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
513d4f81383SUlf Hansson 				NULL);
514c8f0ea45SGeert Uytterhoeven 	return ret;
515c8f0ea45SGeert Uytterhoeven }
516c8f0ea45SGeert Uytterhoeven 
51786e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
518c8f0ea45SGeert Uytterhoeven {
519fc5cbf0cSAxel Haslam 	unsigned int state_idx = genpd->state_idx;
520c8f0ea45SGeert Uytterhoeven 	ktime_t time_start;
521c8f0ea45SGeert Uytterhoeven 	s64 elapsed_ns;
522330e3932SUlf Hansson 	int ret;
523d4f81383SUlf Hansson 
524d4f81383SUlf Hansson 	/* Notify consumers that we are about to power off. */
525330e3932SUlf Hansson 	ret = raw_notifier_call_chain_robust(&genpd->power_notifiers,
526330e3932SUlf Hansson 					     GENPD_NOTIFY_PRE_OFF,
527330e3932SUlf Hansson 					     GENPD_NOTIFY_ON, NULL);
528d4f81383SUlf Hansson 	ret = notifier_to_errno(ret);
529d4f81383SUlf Hansson 	if (ret)
530330e3932SUlf Hansson 		return ret;
531c8f0ea45SGeert Uytterhoeven 
532c8f0ea45SGeert Uytterhoeven 	if (!genpd->power_off)
533d4f81383SUlf Hansson 		goto out;
534c8f0ea45SGeert Uytterhoeven 
535d4f81383SUlf Hansson 	if (!timed) {
536d4f81383SUlf Hansson 		ret = genpd->power_off(genpd);
537d4f81383SUlf Hansson 		if (ret)
538d4f81383SUlf Hansson 			goto busy;
539d4f81383SUlf Hansson 
540d4f81383SUlf Hansson 		goto out;
541d4f81383SUlf Hansson 	}
542a4630c61SGeert Uytterhoeven 
543c8f0ea45SGeert Uytterhoeven 	time_start = ktime_get();
544c8f0ea45SGeert Uytterhoeven 	ret = genpd->power_off(genpd);
5450cec68a9SAisheng Dong 	if (ret)
546d4f81383SUlf Hansson 		goto busy;
547c8f0ea45SGeert Uytterhoeven 
548c8f0ea45SGeert Uytterhoeven 	elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
549fc5cbf0cSAxel Haslam 	if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
550d4f81383SUlf Hansson 		goto out;
551c8f0ea45SGeert Uytterhoeven 
552fc5cbf0cSAxel Haslam 	genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
553c8f0ea45SGeert Uytterhoeven 	genpd->max_off_time_changed = true;
5546d7d5c32SRussell King 	pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
555c8f0ea45SGeert Uytterhoeven 		 genpd->name, "off", elapsed_ns);
556c8f0ea45SGeert Uytterhoeven 
557d4f81383SUlf Hansson out:
558d4f81383SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
559d4f81383SUlf Hansson 				NULL);
5600cec68a9SAisheng Dong 	return 0;
561d4f81383SUlf Hansson busy:
562330e3932SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
563d4f81383SUlf Hansson 	return ret;
564c8f0ea45SGeert Uytterhoeven }
565c8f0ea45SGeert Uytterhoeven 
566f721889fSRafael J. Wysocki /**
56786e12eacSUlf Hansson  * genpd_queue_power_off_work - Queue up the execution of genpd_power_off().
568a3d09c73SMoritz Fischer  * @genpd: PM domain to power off.
56929e47e21SUlf Hansson  *
57086e12eacSUlf Hansson  * Queue up the execution of genpd_power_off() unless it's already been done
57129e47e21SUlf Hansson  * before.
57229e47e21SUlf Hansson  */
57329e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
57429e47e21SUlf Hansson {
57529e47e21SUlf Hansson 	queue_work(pm_wq, &genpd->power_off_work);
57629e47e21SUlf Hansson }
57729e47e21SUlf Hansson 
57829e47e21SUlf Hansson /**
5791f8728b7SUlf Hansson  * genpd_power_off - Remove power from a given PM domain.
5801f8728b7SUlf Hansson  * @genpd: PM domain to power down.
5813c64649dSUlf Hansson  * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the
5823c64649dSUlf Hansson  * RPM status of the releated device is in an intermediate state, not yet turned
5833c64649dSUlf Hansson  * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not
5843c64649dSUlf Hansson  * be RPM_SUSPENDED, while it tries to power off the PM domain.
585763663c9SYang Yingliang  * @depth: nesting count for lockdep.
5861f8728b7SUlf Hansson  *
5871f8728b7SUlf Hansson  * If all of the @genpd's devices have been suspended and all of its subdomains
5881f8728b7SUlf Hansson  * have been powered down, remove power from @genpd.
5891f8728b7SUlf Hansson  */
5902da83545SUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
5912da83545SUlf Hansson 			   unsigned int depth)
5921f8728b7SUlf Hansson {
5931f8728b7SUlf Hansson 	struct pm_domain_data *pdd;
5941f8728b7SUlf Hansson 	struct gpd_link *link;
5951f8728b7SUlf Hansson 	unsigned int not_suspended = 0;
596f63816e4SUlf Hansson 	int ret;
5971f8728b7SUlf Hansson 
5981f8728b7SUlf Hansson 	/*
5991f8728b7SUlf Hansson 	 * Do not try to power off the domain in the following situations:
6001f8728b7SUlf Hansson 	 * (1) The domain is already in the "power off" state.
6011f8728b7SUlf Hansson 	 * (2) System suspend is in progress.
6021f8728b7SUlf Hansson 	 */
60341e2c8e0SUlf Hansson 	if (!genpd_status_on(genpd) || genpd->prepared_count > 0)
6041f8728b7SUlf Hansson 		return 0;
6051f8728b7SUlf Hansson 
606ffaa42e8SUlf Hansson 	/*
607ffaa42e8SUlf Hansson 	 * Abort power off for the PM domain in the following situations:
608ffaa42e8SUlf Hansson 	 * (1) The domain is configured as always on.
609ffaa42e8SUlf Hansson 	 * (2) When the domain has a subdomain being powered on.
610ffaa42e8SUlf Hansson 	 */
611ed61e18aSLeonard Crestez 	if (genpd_is_always_on(genpd) ||
612ed61e18aSLeonard Crestez 			genpd_is_rpm_always_on(genpd) ||
613ed61e18aSLeonard Crestez 			atomic_read(&genpd->sd_count) > 0)
6141f8728b7SUlf Hansson 		return -EBUSY;
6151f8728b7SUlf Hansson 
6161f8728b7SUlf Hansson 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
6171f8728b7SUlf Hansson 		enum pm_qos_flags_status stat;
6181f8728b7SUlf Hansson 
61920f97cafSRafael J. Wysocki 		stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF);
6201f8728b7SUlf Hansson 		if (stat > PM_QOS_FLAGS_NONE)
6211f8728b7SUlf Hansson 			return -EBUSY;
6221f8728b7SUlf Hansson 
6231f8728b7SUlf Hansson 		/*
6241f8728b7SUlf Hansson 		 * Do not allow PM domain to be powered off, when an IRQ safe
6251f8728b7SUlf Hansson 		 * device is part of a non-IRQ safe domain.
6261f8728b7SUlf Hansson 		 */
6271f8728b7SUlf Hansson 		if (!pm_runtime_suspended(pdd->dev) ||
6281f8728b7SUlf Hansson 			irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd))
6291f8728b7SUlf Hansson 			not_suspended++;
6301f8728b7SUlf Hansson 	}
6311f8728b7SUlf Hansson 
6323c64649dSUlf Hansson 	if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
6331f8728b7SUlf Hansson 		return -EBUSY;
6341f8728b7SUlf Hansson 
6351f8728b7SUlf Hansson 	if (genpd->gov && genpd->gov->power_down_ok) {
6361f8728b7SUlf Hansson 		if (!genpd->gov->power_down_ok(&genpd->domain))
6371f8728b7SUlf Hansson 			return -EAGAIN;
6381f8728b7SUlf Hansson 	}
6391f8728b7SUlf Hansson 
6402c9b7f87SUlf Hansson 	/* Default to shallowest state. */
6412c9b7f87SUlf Hansson 	if (!genpd->gov)
6422c9b7f87SUlf Hansson 		genpd->state_idx = 0;
6432c9b7f87SUlf Hansson 
644f63816e4SUlf Hansson 	/* Don't power off, if a child domain is waiting to power on. */
6451f8728b7SUlf Hansson 	if (atomic_read(&genpd->sd_count) > 0)
6461f8728b7SUlf Hansson 		return -EBUSY;
6471f8728b7SUlf Hansson 
6481f8728b7SUlf Hansson 	ret = _genpd_power_off(genpd, true);
649c6a113b5SLina Iyer 	if (ret) {
650c6a113b5SLina Iyer 		genpd->states[genpd->state_idx].rejected++;
6511f8728b7SUlf Hansson 		return ret;
652c6a113b5SLina Iyer 	}
6531f8728b7SUlf Hansson 
65449f618e1SUlf Hansson 	genpd->status = GENPD_STATE_OFF;
655afece3abSThara Gopinath 	genpd_update_accounting(genpd);
656c6a113b5SLina Iyer 	genpd->states[genpd->state_idx].usage++;
6571f8728b7SUlf Hansson 
6588d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
6598d87ae48SKees Cook 		genpd_sd_counter_dec(link->parent);
6608d87ae48SKees Cook 		genpd_lock_nested(link->parent, depth + 1);
6618d87ae48SKees Cook 		genpd_power_off(link->parent, false, depth + 1);
6628d87ae48SKees Cook 		genpd_unlock(link->parent);
6631f8728b7SUlf Hansson 	}
6641f8728b7SUlf Hansson 
6651f8728b7SUlf Hansson 	return 0;
6661f8728b7SUlf Hansson }
6671f8728b7SUlf Hansson 
6681f8728b7SUlf Hansson /**
6698d87ae48SKees Cook  * genpd_power_on - Restore power to a given PM domain and its parents.
6705248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
6710106ef51SMarek Szyprowski  * @depth: nesting count for lockdep.
6725248051bSRafael J. Wysocki  *
6738d87ae48SKees Cook  * Restore power to @genpd and all of its parents so that it is possible to
6745248051bSRafael J. Wysocki  * resume a device belonging to it.
6755248051bSRafael J. Wysocki  */
67686e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
6775248051bSRafael J. Wysocki {
6785063ce15SRafael J. Wysocki 	struct gpd_link *link;
6795248051bSRafael J. Wysocki 	int ret = 0;
6805248051bSRafael J. Wysocki 
68141e2c8e0SUlf Hansson 	if (genpd_status_on(genpd))
6823f241775SRafael J. Wysocki 		return 0;
6835248051bSRafael J. Wysocki 
6845063ce15SRafael J. Wysocki 	/*
6855063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
6868d87ae48SKees Cook 	 * executed, unless one of the parents' .power_on() callbacks fiddles
6875063ce15SRafael J. Wysocki 	 * with it.
6885063ce15SRafael J. Wysocki 	 */
6898d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
6908d87ae48SKees Cook 		struct generic_pm_domain *parent = link->parent;
6915248051bSRafael J. Wysocki 
6928d87ae48SKees Cook 		genpd_sd_counter_inc(parent);
6930106ef51SMarek Szyprowski 
6948d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
6958d87ae48SKees Cook 		ret = genpd_power_on(parent, depth + 1);
6968d87ae48SKees Cook 		genpd_unlock(parent);
6970106ef51SMarek Szyprowski 
6985063ce15SRafael J. Wysocki 		if (ret) {
6998d87ae48SKees Cook 			genpd_sd_counter_dec(parent);
7009e08cf42SRafael J. Wysocki 			goto err;
7015248051bSRafael J. Wysocki 		}
7025063ce15SRafael J. Wysocki 	}
7035248051bSRafael J. Wysocki 
70486e12eacSUlf Hansson 	ret = _genpd_power_on(genpd, true);
7059e08cf42SRafael J. Wysocki 	if (ret)
7069e08cf42SRafael J. Wysocki 		goto err;
7070140d8bdSRafael J. Wysocki 
70849f618e1SUlf Hansson 	genpd->status = GENPD_STATE_ON;
709afece3abSThara Gopinath 	genpd_update_accounting(genpd);
710afece3abSThara Gopinath 
7113f241775SRafael J. Wysocki 	return 0;
7129e08cf42SRafael J. Wysocki 
7139e08cf42SRafael J. Wysocki  err:
71429e47e21SUlf Hansson 	list_for_each_entry_continue_reverse(link,
7158d87ae48SKees Cook 					&genpd->child_links,
7168d87ae48SKees Cook 					child_node) {
7178d87ae48SKees Cook 		genpd_sd_counter_dec(link->parent);
7188d87ae48SKees Cook 		genpd_lock_nested(link->parent, depth + 1);
7198d87ae48SKees Cook 		genpd_power_off(link->parent, false, depth + 1);
7208d87ae48SKees Cook 		genpd_unlock(link->parent);
72129e47e21SUlf Hansson 	}
7229e08cf42SRafael J. Wysocki 
7233f241775SRafael J. Wysocki 	return ret;
7243f241775SRafael J. Wysocki }
7253f241775SRafael J. Wysocki 
726ea71c596SUlf Hansson static int genpd_dev_pm_start(struct device *dev)
727ea71c596SUlf Hansson {
728ea71c596SUlf Hansson 	struct generic_pm_domain *genpd = dev_to_genpd(dev);
729ea71c596SUlf Hansson 
730ea71c596SUlf Hansson 	return genpd_start_dev(genpd, dev);
731ea71c596SUlf Hansson }
732ea71c596SUlf Hansson 
7336ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
7346ff7bb0dSRafael J. Wysocki 				     unsigned long val, void *ptr)
7356ff7bb0dSRafael J. Wysocki {
7366ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
7376ff7bb0dSRafael J. Wysocki 	struct device *dev;
7386ff7bb0dSRafael J. Wysocki 
7396ff7bb0dSRafael J. Wysocki 	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
7406ff7bb0dSRafael J. Wysocki 	dev = gpd_data->base.dev;
7416ff7bb0dSRafael J. Wysocki 
7426ff7bb0dSRafael J. Wysocki 	for (;;) {
7436ff7bb0dSRafael J. Wysocki 		struct generic_pm_domain *genpd;
7446ff7bb0dSRafael J. Wysocki 		struct pm_domain_data *pdd;
7456ff7bb0dSRafael J. Wysocki 
7466ff7bb0dSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
7476ff7bb0dSRafael J. Wysocki 
7486ff7bb0dSRafael J. Wysocki 		pdd = dev->power.subsys_data ?
7496ff7bb0dSRafael J. Wysocki 				dev->power.subsys_data->domain_data : NULL;
750b4883ca4SViresh Kumar 		if (pdd) {
7516ff7bb0dSRafael J. Wysocki 			to_gpd_data(pdd)->td.constraint_changed = true;
7526ff7bb0dSRafael J. Wysocki 			genpd = dev_to_genpd(dev);
7536ff7bb0dSRafael J. Wysocki 		} else {
7546ff7bb0dSRafael J. Wysocki 			genpd = ERR_PTR(-ENODATA);
7556ff7bb0dSRafael J. Wysocki 		}
7566ff7bb0dSRafael J. Wysocki 
7576ff7bb0dSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
7586ff7bb0dSRafael J. Wysocki 
7596ff7bb0dSRafael J. Wysocki 		if (!IS_ERR(genpd)) {
76035241d12SLina Iyer 			genpd_lock(genpd);
7616ff7bb0dSRafael J. Wysocki 			genpd->max_off_time_changed = true;
76235241d12SLina Iyer 			genpd_unlock(genpd);
7636ff7bb0dSRafael J. Wysocki 		}
7646ff7bb0dSRafael J. Wysocki 
7656ff7bb0dSRafael J. Wysocki 		dev = dev->parent;
7666ff7bb0dSRafael J. Wysocki 		if (!dev || dev->power.ignore_children)
7676ff7bb0dSRafael J. Wysocki 			break;
7686ff7bb0dSRafael J. Wysocki 	}
7696ff7bb0dSRafael J. Wysocki 
7706ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
7716ff7bb0dSRafael J. Wysocki }
7726ff7bb0dSRafael J. Wysocki 
7735248051bSRafael J. Wysocki /**
774f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
775f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
776f721889fSRafael J. Wysocki  */
777f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
778f721889fSRafael J. Wysocki {
779f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
780f721889fSRafael J. Wysocki 
781f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
782f721889fSRafael J. Wysocki 
78335241d12SLina Iyer 	genpd_lock(genpd);
7842da83545SUlf Hansson 	genpd_power_off(genpd, false, 0);
78535241d12SLina Iyer 	genpd_unlock(genpd);
786f721889fSRafael J. Wysocki }
787f721889fSRafael J. Wysocki 
788f721889fSRafael J. Wysocki /**
78954eeddbfSUlf Hansson  * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks
79054eeddbfSUlf Hansson  * @dev: Device to handle.
79154eeddbfSUlf Hansson  */
79254eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev)
79354eeddbfSUlf Hansson {
79454eeddbfSUlf Hansson 	int (*cb)(struct device *__dev);
79554eeddbfSUlf Hansson 
79654eeddbfSUlf Hansson 	if (dev->type && dev->type->pm)
79754eeddbfSUlf Hansson 		cb = dev->type->pm->runtime_suspend;
79854eeddbfSUlf Hansson 	else if (dev->class && dev->class->pm)
79954eeddbfSUlf Hansson 		cb = dev->class->pm->runtime_suspend;
80054eeddbfSUlf Hansson 	else if (dev->bus && dev->bus->pm)
80154eeddbfSUlf Hansson 		cb = dev->bus->pm->runtime_suspend;
80254eeddbfSUlf Hansson 	else
80354eeddbfSUlf Hansson 		cb = NULL;
80454eeddbfSUlf Hansson 
80554eeddbfSUlf Hansson 	if (!cb && dev->driver && dev->driver->pm)
80654eeddbfSUlf Hansson 		cb = dev->driver->pm->runtime_suspend;
80754eeddbfSUlf Hansson 
80854eeddbfSUlf Hansson 	return cb ? cb(dev) : 0;
80954eeddbfSUlf Hansson }
81054eeddbfSUlf Hansson 
81154eeddbfSUlf Hansson /**
81254eeddbfSUlf Hansson  * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks
81354eeddbfSUlf Hansson  * @dev: Device to handle.
81454eeddbfSUlf Hansson  */
81554eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev)
81654eeddbfSUlf Hansson {
81754eeddbfSUlf Hansson 	int (*cb)(struct device *__dev);
81854eeddbfSUlf Hansson 
81954eeddbfSUlf Hansson 	if (dev->type && dev->type->pm)
82054eeddbfSUlf Hansson 		cb = dev->type->pm->runtime_resume;
82154eeddbfSUlf Hansson 	else if (dev->class && dev->class->pm)
82254eeddbfSUlf Hansson 		cb = dev->class->pm->runtime_resume;
82354eeddbfSUlf Hansson 	else if (dev->bus && dev->bus->pm)
82454eeddbfSUlf Hansson 		cb = dev->bus->pm->runtime_resume;
82554eeddbfSUlf Hansson 	else
82654eeddbfSUlf Hansson 		cb = NULL;
82754eeddbfSUlf Hansson 
82854eeddbfSUlf Hansson 	if (!cb && dev->driver && dev->driver->pm)
82954eeddbfSUlf Hansson 		cb = dev->driver->pm->runtime_resume;
83054eeddbfSUlf Hansson 
83154eeddbfSUlf Hansson 	return cb ? cb(dev) : 0;
83254eeddbfSUlf Hansson }
83354eeddbfSUlf Hansson 
83454eeddbfSUlf Hansson /**
835795bd2e7SUlf Hansson  * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
836f721889fSRafael J. Wysocki  * @dev: Device to suspend.
837f721889fSRafael J. Wysocki  *
838f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
839f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
840f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
841f721889fSRafael J. Wysocki  */
842795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev)
843f721889fSRafael J. Wysocki {
844f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
8459df3921eSUlf Hansson 	bool (*suspend_ok)(struct device *__dev);
8462b1d88cdSUlf Hansson 	struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
847ffe12855SUlf Hansson 	bool runtime_pm = pm_runtime_enabled(dev);
8482b1d88cdSUlf Hansson 	ktime_t time_start;
8492b1d88cdSUlf Hansson 	s64 elapsed_ns;
850d5e4cbfeSRafael J. Wysocki 	int ret;
851f721889fSRafael J. Wysocki 
852f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
853f721889fSRafael J. Wysocki 
8545248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
8555248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
856f721889fSRafael J. Wysocki 		return -EINVAL;
857f721889fSRafael J. Wysocki 
858ffe12855SUlf Hansson 	/*
859ffe12855SUlf Hansson 	 * A runtime PM centric subsystem/driver may re-use the runtime PM
860ffe12855SUlf Hansson 	 * callbacks for other purposes than runtime PM. In those scenarios
861ffe12855SUlf Hansson 	 * runtime PM is disabled. Under these circumstances, we shall skip
862ffe12855SUlf Hansson 	 * validating/measuring the PM QoS latency.
863ffe12855SUlf Hansson 	 */
8649df3921eSUlf Hansson 	suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL;
8659df3921eSUlf Hansson 	if (runtime_pm && suspend_ok && !suspend_ok(dev))
866b02c999aSRafael J. Wysocki 		return -EBUSY;
867b02c999aSRafael J. Wysocki 
8682b1d88cdSUlf Hansson 	/* Measure suspend latency. */
869d33d5a6cSLinus Torvalds 	time_start = 0;
870ffe12855SUlf Hansson 	if (runtime_pm)
8712b1d88cdSUlf Hansson 		time_start = ktime_get();
8722b1d88cdSUlf Hansson 
87354eeddbfSUlf Hansson 	ret = __genpd_runtime_suspend(dev);
874f721889fSRafael J. Wysocki 	if (ret)
87517b75ecaSRafael J. Wysocki 		return ret;
87617b75ecaSRafael J. Wysocki 
8772b1d88cdSUlf Hansson 	ret = genpd_stop_dev(genpd, dev);
878ba2bbfbfSUlf Hansson 	if (ret) {
87954eeddbfSUlf Hansson 		__genpd_runtime_resume(dev);
880ba2bbfbfSUlf Hansson 		return ret;
881ba2bbfbfSUlf Hansson 	}
882ba2bbfbfSUlf Hansson 
8832b1d88cdSUlf Hansson 	/* Update suspend latency value if the measured time exceeds it. */
884ffe12855SUlf Hansson 	if (runtime_pm) {
8852b1d88cdSUlf Hansson 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
8862b1d88cdSUlf Hansson 		if (elapsed_ns > td->suspend_latency_ns) {
8872b1d88cdSUlf Hansson 			td->suspend_latency_ns = elapsed_ns;
8882b1d88cdSUlf Hansson 			dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
8892b1d88cdSUlf Hansson 				elapsed_ns);
8902b1d88cdSUlf Hansson 			genpd->max_off_time_changed = true;
8912b1d88cdSUlf Hansson 			td->constraint_changed = true;
8922b1d88cdSUlf Hansson 		}
893ffe12855SUlf Hansson 	}
8942b1d88cdSUlf Hansson 
8950aa2a221SRafael J. Wysocki 	/*
896d716f479SLina Iyer 	 * If power.irq_safe is set, this routine may be run with
897d716f479SLina Iyer 	 * IRQs disabled, so suspend only if the PM domain also is irq_safe.
8980aa2a221SRafael J. Wysocki 	 */
899d716f479SLina Iyer 	if (irq_safe_dev_in_no_sleep_domain(dev, genpd))
9000aa2a221SRafael J. Wysocki 		return 0;
9010aa2a221SRafael J. Wysocki 
90235241d12SLina Iyer 	genpd_lock(genpd);
9032da83545SUlf Hansson 	genpd_power_off(genpd, true, 0);
90435241d12SLina Iyer 	genpd_unlock(genpd);
905f721889fSRafael J. Wysocki 
906f721889fSRafael J. Wysocki 	return 0;
907f721889fSRafael J. Wysocki }
908f721889fSRafael J. Wysocki 
909f721889fSRafael J. Wysocki /**
910795bd2e7SUlf Hansson  * genpd_runtime_resume - Resume a device belonging to I/O PM domain.
911f721889fSRafael J. Wysocki  * @dev: Device to resume.
912f721889fSRafael J. Wysocki  *
913f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
914f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
915f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
916f721889fSRafael J. Wysocki  */
917795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev)
918f721889fSRafael J. Wysocki {
919f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
9202b1d88cdSUlf Hansson 	struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
921ffe12855SUlf Hansson 	bool runtime_pm = pm_runtime_enabled(dev);
9222b1d88cdSUlf Hansson 	ktime_t time_start;
9232b1d88cdSUlf Hansson 	s64 elapsed_ns;
924f721889fSRafael J. Wysocki 	int ret;
925ba2bbfbfSUlf Hansson 	bool timed = true;
926f721889fSRafael J. Wysocki 
927f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
928f721889fSRafael J. Wysocki 
9295248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9305248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
931f721889fSRafael J. Wysocki 		return -EINVAL;
932f721889fSRafael J. Wysocki 
933d716f479SLina Iyer 	/*
934d716f479SLina Iyer 	 * As we don't power off a non IRQ safe domain, which holds
935d716f479SLina Iyer 	 * an IRQ safe device, we don't need to restore power to it.
936d716f479SLina Iyer 	 */
937d716f479SLina Iyer 	if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) {
938ba2bbfbfSUlf Hansson 		timed = false;
939ba2bbfbfSUlf Hansson 		goto out;
940ba2bbfbfSUlf Hansson 	}
9410aa2a221SRafael J. Wysocki 
94235241d12SLina Iyer 	genpd_lock(genpd);
94386e12eacSUlf Hansson 	ret = genpd_power_on(genpd, 0);
94435241d12SLina Iyer 	genpd_unlock(genpd);
945ba2bbfbfSUlf Hansson 
946ba2bbfbfSUlf Hansson 	if (ret)
9473f241775SRafael J. Wysocki 		return ret;
948c6d22b37SRafael J. Wysocki 
949ba2bbfbfSUlf Hansson  out:
9502b1d88cdSUlf Hansson 	/* Measure resume latency. */
951ab51e6baSAugusto Mecking Caringi 	time_start = 0;
952ffe12855SUlf Hansson 	if (timed && runtime_pm)
9532b1d88cdSUlf Hansson 		time_start = ktime_get();
9542b1d88cdSUlf Hansson 
955076395caSLaurent Pinchart 	ret = genpd_start_dev(genpd, dev);
956076395caSLaurent Pinchart 	if (ret)
957076395caSLaurent Pinchart 		goto err_poweroff;
958076395caSLaurent Pinchart 
95954eeddbfSUlf Hansson 	ret = __genpd_runtime_resume(dev);
960076395caSLaurent Pinchart 	if (ret)
961076395caSLaurent Pinchart 		goto err_stop;
9622b1d88cdSUlf Hansson 
9632b1d88cdSUlf Hansson 	/* Update resume latency value if the measured time exceeds it. */
964ffe12855SUlf Hansson 	if (timed && runtime_pm) {
9652b1d88cdSUlf Hansson 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
9662b1d88cdSUlf Hansson 		if (elapsed_ns > td->resume_latency_ns) {
9672b1d88cdSUlf Hansson 			td->resume_latency_ns = elapsed_ns;
9682b1d88cdSUlf Hansson 			dev_dbg(dev, "resume latency exceeded, %lld ns\n",
9692b1d88cdSUlf Hansson 				elapsed_ns);
9702b1d88cdSUlf Hansson 			genpd->max_off_time_changed = true;
9712b1d88cdSUlf Hansson 			td->constraint_changed = true;
9722b1d88cdSUlf Hansson 		}
9732b1d88cdSUlf Hansson 	}
97417b75ecaSRafael J. Wysocki 
975f721889fSRafael J. Wysocki 	return 0;
976076395caSLaurent Pinchart 
977076395caSLaurent Pinchart err_stop:
978076395caSLaurent Pinchart 	genpd_stop_dev(genpd, dev);
979076395caSLaurent Pinchart err_poweroff:
9806dc466d3SAbaci Team 	if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) {
98135241d12SLina Iyer 		genpd_lock(genpd);
9822da83545SUlf Hansson 		genpd_power_off(genpd, true, 0);
98335241d12SLina Iyer 		genpd_unlock(genpd);
984076395caSLaurent Pinchart 	}
985076395caSLaurent Pinchart 
986076395caSLaurent Pinchart 	return ret;
987f721889fSRafael J. Wysocki }
988f721889fSRafael J. Wysocki 
98939ac5ba5STushar Behera static bool pd_ignore_unused;
99039ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused)
99139ac5ba5STushar Behera {
99239ac5ba5STushar Behera 	pd_ignore_unused = true;
99339ac5ba5STushar Behera 	return 1;
99439ac5ba5STushar Behera }
99539ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup);
99639ac5ba5STushar Behera 
99717f2ae7fSRafael J. Wysocki /**
99886e12eacSUlf Hansson  * genpd_power_off_unused - Power off all PM domains with no devices in use.
99917f2ae7fSRafael J. Wysocki  */
100086e12eacSUlf Hansson static int __init genpd_power_off_unused(void)
100117f2ae7fSRafael J. Wysocki {
100217f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
100317f2ae7fSRafael J. Wysocki 
100439ac5ba5STushar Behera 	if (pd_ignore_unused) {
100539ac5ba5STushar Behera 		pr_warn("genpd: Not disabling unused power domains\n");
1006bb4b72fcSUlf Hansson 		return 0;
100739ac5ba5STushar Behera 	}
100839ac5ba5STushar Behera 
100917f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
101017f2ae7fSRafael J. Wysocki 
101117f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
101217f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
101317f2ae7fSRafael J. Wysocki 
101417f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
101517f2ae7fSRafael J. Wysocki 
10162fe71dcdSUlf Hansson 	return 0;
10172fe71dcdSUlf Hansson }
101886e12eacSUlf Hansson late_initcall(genpd_power_off_unused);
10192fe71dcdSUlf Hansson 
10200159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP
10210159ec67SJon Hunter 
1022596ba34bSRafael J. Wysocki /**
10238d87ae48SKees Cook  * genpd_sync_power_off - Synchronously power off a PM domain and its parents.
1024596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
10250883ac03SUlf Hansson  * @use_lock: use the lock.
10260883ac03SUlf Hansson  * @depth: nesting count for lockdep.
1027596ba34bSRafael J. Wysocki  *
1028596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
10298d87ae48SKees Cook  * hibernation) and do that if so.  Also, in that case propagate to its parents.
1030596ba34bSRafael J. Wysocki  *
103177f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
10320883ac03SUlf Hansson  * transitions. The "noirq" callbacks may be executed asynchronously, thus in
10330883ac03SUlf Hansson  * these cases the lock must be held.
1034596ba34bSRafael J. Wysocki  */
10350883ac03SUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
10360883ac03SUlf Hansson 				 unsigned int depth)
1037596ba34bSRafael J. Wysocki {
10385063ce15SRafael J. Wysocki 	struct gpd_link *link;
1039596ba34bSRafael J. Wysocki 
1040ffaa42e8SUlf Hansson 	if (!genpd_status_on(genpd) || genpd_is_always_on(genpd))
1041596ba34bSRafael J. Wysocki 		return;
1042596ba34bSRafael J. Wysocki 
1043c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
1044c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
1045596ba34bSRafael J. Wysocki 		return;
1046596ba34bSRafael J. Wysocki 
1047fc5cbf0cSAxel Haslam 	/* Choose the deepest state when suspending */
1048fc5cbf0cSAxel Haslam 	genpd->state_idx = genpd->state_count - 1;
10491c14967cSUlf Hansson 	if (_genpd_power_off(genpd, false))
10501c14967cSUlf Hansson 		return;
1051596ba34bSRafael J. Wysocki 
105249f618e1SUlf Hansson 	genpd->status = GENPD_STATE_OFF;
10535063ce15SRafael J. Wysocki 
10548d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
10558d87ae48SKees Cook 		genpd_sd_counter_dec(link->parent);
10560883ac03SUlf Hansson 
10570883ac03SUlf Hansson 		if (use_lock)
10588d87ae48SKees Cook 			genpd_lock_nested(link->parent, depth + 1);
10590883ac03SUlf Hansson 
10608d87ae48SKees Cook 		genpd_sync_power_off(link->parent, use_lock, depth + 1);
10610883ac03SUlf Hansson 
10620883ac03SUlf Hansson 		if (use_lock)
10638d87ae48SKees Cook 			genpd_unlock(link->parent);
1064596ba34bSRafael J. Wysocki 	}
1065596ba34bSRafael J. Wysocki }
1066596ba34bSRafael J. Wysocki 
1067596ba34bSRafael J. Wysocki /**
10688d87ae48SKees Cook  * genpd_sync_power_on - Synchronously power on a PM domain and its parents.
1069802d8b49SRafael J. Wysocki  * @genpd: PM domain to power on.
10700883ac03SUlf Hansson  * @use_lock: use the lock.
10710883ac03SUlf Hansson  * @depth: nesting count for lockdep.
1072802d8b49SRafael J. Wysocki  *
107377f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
10740883ac03SUlf Hansson  * transitions. The "noirq" callbacks may be executed asynchronously, thus in
10750883ac03SUlf Hansson  * these cases the lock must be held.
1076802d8b49SRafael J. Wysocki  */
10770883ac03SUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
10780883ac03SUlf Hansson 				unsigned int depth)
1079802d8b49SRafael J. Wysocki {
1080802d8b49SRafael J. Wysocki 	struct gpd_link *link;
1081802d8b49SRafael J. Wysocki 
108241e2c8e0SUlf Hansson 	if (genpd_status_on(genpd))
1083802d8b49SRafael J. Wysocki 		return;
1084802d8b49SRafael J. Wysocki 
10858d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
10868d87ae48SKees Cook 		genpd_sd_counter_inc(link->parent);
10870883ac03SUlf Hansson 
10880883ac03SUlf Hansson 		if (use_lock)
10898d87ae48SKees Cook 			genpd_lock_nested(link->parent, depth + 1);
10900883ac03SUlf Hansson 
10918d87ae48SKees Cook 		genpd_sync_power_on(link->parent, use_lock, depth + 1);
10920883ac03SUlf Hansson 
10930883ac03SUlf Hansson 		if (use_lock)
10948d87ae48SKees Cook 			genpd_unlock(link->parent);
1095802d8b49SRafael J. Wysocki 	}
1096802d8b49SRafael J. Wysocki 
109786e12eacSUlf Hansson 	_genpd_power_on(genpd, false);
109849f618e1SUlf Hansson 	genpd->status = GENPD_STATE_ON;
1099802d8b49SRafael J. Wysocki }
1100802d8b49SRafael J. Wysocki 
1101802d8b49SRafael J. Wysocki /**
11029e9704eaSUlf Hansson  * genpd_prepare - Start power transition of a device in a PM domain.
1103596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
1104596ba34bSRafael J. Wysocki  *
1105596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
1106596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
1107596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
1108596ba34bSRafael J. Wysocki  * consisting of I/O devices.
1109596ba34bSRafael J. Wysocki  */
11109e9704eaSUlf Hansson static int genpd_prepare(struct device *dev)
1111596ba34bSRafael J. Wysocki {
1112596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1113b6c10c84SRafael J. Wysocki 	int ret;
1114596ba34bSRafael J. Wysocki 
1115596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1116596ba34bSRafael J. Wysocki 
1117596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1118596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1119596ba34bSRafael J. Wysocki 		return -EINVAL;
1120596ba34bSRafael J. Wysocki 
112135241d12SLina Iyer 	genpd_lock(genpd);
1122596ba34bSRafael J. Wysocki 
112339dd0f23SUlf Hansson 	if (genpd->prepared_count++ == 0)
112465533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
112517b75ecaSRafael J. Wysocki 
112635241d12SLina Iyer 	genpd_unlock(genpd);
1127596ba34bSRafael J. Wysocki 
1128b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
11295241ab40SUlf Hansson 	if (ret < 0) {
113035241d12SLina Iyer 		genpd_lock(genpd);
1131b6c10c84SRafael J. Wysocki 
113239dd0f23SUlf Hansson 		genpd->prepared_count--;
1133b6c10c84SRafael J. Wysocki 
113435241d12SLina Iyer 		genpd_unlock(genpd);
1135b6c10c84SRafael J. Wysocki 	}
113617b75ecaSRafael J. Wysocki 
11375241ab40SUlf Hansson 	/* Never return 1, as genpd don't cope with the direct_complete path. */
11385241ab40SUlf Hansson 	return ret >= 0 ? 0 : ret;
1139596ba34bSRafael J. Wysocki }
1140596ba34bSRafael J. Wysocki 
1141596ba34bSRafael J. Wysocki /**
114210da6542SMikko Perttunen  * genpd_finish_suspend - Completion of suspend or hibernation of device in an
114310da6542SMikko Perttunen  *   I/O pm domain.
11440496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
114510da6542SMikko Perttunen  * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback.
11460496c8aeSRafael J. Wysocki  *
11470496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
11480496c8aeSRafael J. Wysocki  * been stopped.
11490496c8aeSRafael J. Wysocki  */
115010da6542SMikko Perttunen static int genpd_finish_suspend(struct device *dev, bool poweroff)
11510496c8aeSRafael J. Wysocki {
11520496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1153a935424bSUlf Hansson 	int ret = 0;
1154596ba34bSRafael J. Wysocki 
11550496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
11560496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
11570496c8aeSRafael J. Wysocki 		return -EINVAL;
11580496c8aeSRafael J. Wysocki 
115910da6542SMikko Perttunen 	if (poweroff)
116010da6542SMikko Perttunen 		ret = pm_generic_poweroff_noirq(dev);
116110da6542SMikko Perttunen 	else
116210da6542SMikko Perttunen 		ret = pm_generic_suspend_noirq(dev);
116310da6542SMikko Perttunen 	if (ret)
116410da6542SMikko Perttunen 		return ret;
116510da6542SMikko Perttunen 
11664e1d9a73SPatrice Chotard 	if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
1167a935424bSUlf Hansson 		return 0;
1168a935424bSUlf Hansson 
116917218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
117017218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev)) {
117117218e00SRafael J. Wysocki 		ret = genpd_stop_dev(genpd, dev);
1172a935424bSUlf Hansson 		if (ret) {
1173a935424bSUlf Hansson 			if (poweroff)
1174a935424bSUlf Hansson 				pm_generic_restore_noirq(dev);
1175a935424bSUlf Hansson 			else
1176a935424bSUlf Hansson 				pm_generic_resume_noirq(dev);
1177122a2237SUlf Hansson 			return ret;
1178122a2237SUlf Hansson 		}
1179a935424bSUlf Hansson 	}
1180122a2237SUlf Hansson 
11810883ac03SUlf Hansson 	genpd_lock(genpd);
1182596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
11830883ac03SUlf Hansson 	genpd_sync_power_off(genpd, true, 0);
11840883ac03SUlf Hansson 	genpd_unlock(genpd);
1185596ba34bSRafael J. Wysocki 
1186596ba34bSRafael J. Wysocki 	return 0;
1187596ba34bSRafael J. Wysocki }
1188596ba34bSRafael J. Wysocki 
1189596ba34bSRafael J. Wysocki /**
11909e9704eaSUlf Hansson  * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
119110da6542SMikko Perttunen  * @dev: Device to suspend.
119210da6542SMikko Perttunen  *
119310da6542SMikko Perttunen  * Stop the device and remove power from the domain if all devices in it have
119410da6542SMikko Perttunen  * been stopped.
119510da6542SMikko Perttunen  */
11969e9704eaSUlf Hansson static int genpd_suspend_noirq(struct device *dev)
119710da6542SMikko Perttunen {
119810da6542SMikko Perttunen 	dev_dbg(dev, "%s()\n", __func__);
119910da6542SMikko Perttunen 
120010da6542SMikko Perttunen 	return genpd_finish_suspend(dev, false);
120110da6542SMikko Perttunen }
120210da6542SMikko Perttunen 
120310da6542SMikko Perttunen /**
12049e9704eaSUlf Hansson  * genpd_resume_noirq - Start of resume of device in an I/O PM domain.
1205596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1206596ba34bSRafael J. Wysocki  *
12070496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
1208596ba34bSRafael J. Wysocki  */
12099e9704eaSUlf Hansson static int genpd_resume_noirq(struct device *dev)
1210596ba34bSRafael J. Wysocki {
1211596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1212a935424bSUlf Hansson 	int ret;
1213596ba34bSRafael J. Wysocki 
1214596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1215596ba34bSRafael J. Wysocki 
1216596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1217596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1218596ba34bSRafael J. Wysocki 		return -EINVAL;
1219596ba34bSRafael J. Wysocki 
12204e1d9a73SPatrice Chotard 	if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
1221a935424bSUlf Hansson 		return pm_generic_resume_noirq(dev);
1222596ba34bSRafael J. Wysocki 
12230883ac03SUlf Hansson 	genpd_lock(genpd);
12240883ac03SUlf Hansson 	genpd_sync_power_on(genpd, true, 0);
1225596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
12260883ac03SUlf Hansson 	genpd_unlock(genpd);
1227596ba34bSRafael J. Wysocki 
122817218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
122917218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev)) {
123017218e00SRafael J. Wysocki 		ret = genpd_start_dev(genpd, dev);
123110da6542SMikko Perttunen 		if (ret)
123210da6542SMikko Perttunen 			return ret;
1233a935424bSUlf Hansson 	}
123410da6542SMikko Perttunen 
1235a935424bSUlf Hansson 	return pm_generic_resume_noirq(dev);
1236596ba34bSRafael J. Wysocki }
1237596ba34bSRafael J. Wysocki 
1238596ba34bSRafael J. Wysocki /**
12399e9704eaSUlf Hansson  * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1240596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1241596ba34bSRafael J. Wysocki  *
1242596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1243596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1244596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1245596ba34bSRafael J. Wysocki  * devices.
1246596ba34bSRafael J. Wysocki  */
12479e9704eaSUlf Hansson static int genpd_freeze_noirq(struct device *dev)
1248596ba34bSRafael J. Wysocki {
1249d8600c8bSKrzysztof Kozlowski 	const struct generic_pm_domain *genpd;
1250122a2237SUlf Hansson 	int ret = 0;
1251596ba34bSRafael J. Wysocki 
1252596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1253596ba34bSRafael J. Wysocki 
1254596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1255596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1256596ba34bSRafael J. Wysocki 		return -EINVAL;
1257596ba34bSRafael J. Wysocki 
125810da6542SMikko Perttunen 	ret = pm_generic_freeze_noirq(dev);
125910da6542SMikko Perttunen 	if (ret)
126010da6542SMikko Perttunen 		return ret;
126110da6542SMikko Perttunen 
126217218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
126317218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev))
126417218e00SRafael J. Wysocki 		ret = genpd_stop_dev(genpd, dev);
1265122a2237SUlf Hansson 
1266122a2237SUlf Hansson 	return ret;
1267596ba34bSRafael J. Wysocki }
1268596ba34bSRafael J. Wysocki 
1269596ba34bSRafael J. Wysocki /**
12709e9704eaSUlf Hansson  * genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1271596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1272596ba34bSRafael J. Wysocki  *
12730496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
12740496c8aeSRafael J. Wysocki  * before the system transition.
1275596ba34bSRafael J. Wysocki  */
12769e9704eaSUlf Hansson static int genpd_thaw_noirq(struct device *dev)
1277596ba34bSRafael J. Wysocki {
1278d8600c8bSKrzysztof Kozlowski 	const struct generic_pm_domain *genpd;
1279122a2237SUlf Hansson 	int ret = 0;
1280596ba34bSRafael J. Wysocki 
1281596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1282596ba34bSRafael J. Wysocki 
1283596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1284596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1285596ba34bSRafael J. Wysocki 		return -EINVAL;
1286596ba34bSRafael J. Wysocki 
128717218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
128817218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev)) {
128917218e00SRafael J. Wysocki 		ret = genpd_start_dev(genpd, dev);
129010da6542SMikko Perttunen 		if (ret)
1291122a2237SUlf Hansson 			return ret;
12920496c8aeSRafael J. Wysocki 	}
1293596ba34bSRafael J. Wysocki 
129410da6542SMikko Perttunen 	return pm_generic_thaw_noirq(dev);
129510da6542SMikko Perttunen }
129610da6542SMikko Perttunen 
129710da6542SMikko Perttunen /**
12989e9704eaSUlf Hansson  * genpd_poweroff_noirq - Completion of hibernation of device in an
129910da6542SMikko Perttunen  *   I/O PM domain.
130010da6542SMikko Perttunen  * @dev: Device to poweroff.
130110da6542SMikko Perttunen  *
130210da6542SMikko Perttunen  * Stop the device and remove power from the domain if all devices in it have
130310da6542SMikko Perttunen  * been stopped.
130410da6542SMikko Perttunen  */
13059e9704eaSUlf Hansson static int genpd_poweroff_noirq(struct device *dev)
130610da6542SMikko Perttunen {
130710da6542SMikko Perttunen 	dev_dbg(dev, "%s()\n", __func__);
130810da6542SMikko Perttunen 
130910da6542SMikko Perttunen 	return genpd_finish_suspend(dev, true);
131010da6542SMikko Perttunen }
131110da6542SMikko Perttunen 
13120496c8aeSRafael J. Wysocki /**
13139e9704eaSUlf Hansson  * genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1314596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1315596ba34bSRafael J. Wysocki  *
13160496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
13170496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1318596ba34bSRafael J. Wysocki  */
13199e9704eaSUlf Hansson static int genpd_restore_noirq(struct device *dev)
1320596ba34bSRafael J. Wysocki {
1321596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1322122a2237SUlf Hansson 	int ret = 0;
1323596ba34bSRafael J. Wysocki 
1324596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1325596ba34bSRafael J. Wysocki 
1326596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1327596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1328596ba34bSRafael J. Wysocki 		return -EINVAL;
1329596ba34bSRafael J. Wysocki 
1330596ba34bSRafael J. Wysocki 	/*
133165533bbfSRafael J. Wysocki 	 * At this point suspended_count == 0 means we are being run for the
133265533bbfSRafael J. Wysocki 	 * first time for the given domain in the present cycle.
133365533bbfSRafael J. Wysocki 	 */
13340883ac03SUlf Hansson 	genpd_lock(genpd);
1335505a70b7SGeert Uytterhoeven 	if (genpd->suspended_count++ == 0) {
133665533bbfSRafael J. Wysocki 		/*
133765533bbfSRafael J. Wysocki 		 * The boot kernel might put the domain into arbitrary state,
133886e12eacSUlf Hansson 		 * so make it appear as powered off to genpd_sync_power_on(),
1339802d8b49SRafael J. Wysocki 		 * so that it tries to power it on in case it was really off.
1340596ba34bSRafael J. Wysocki 		 */
134149f618e1SUlf Hansson 		genpd->status = GENPD_STATE_OFF;
1342505a70b7SGeert Uytterhoeven 	}
134318dd2eceSRafael J. Wysocki 
13440883ac03SUlf Hansson 	genpd_sync_power_on(genpd, true, 0);
13450883ac03SUlf Hansson 	genpd_unlock(genpd);
1346596ba34bSRafael J. Wysocki 
134717218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
134817218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev)) {
134917218e00SRafael J. Wysocki 		ret = genpd_start_dev(genpd, dev);
135010da6542SMikko Perttunen 		if (ret)
1351122a2237SUlf Hansson 			return ret;
1352596ba34bSRafael J. Wysocki 	}
1353596ba34bSRafael J. Wysocki 
135410da6542SMikko Perttunen 	return pm_generic_restore_noirq(dev);
135510da6542SMikko Perttunen }
135610da6542SMikko Perttunen 
1357596ba34bSRafael J. Wysocki /**
13589e9704eaSUlf Hansson  * genpd_complete - Complete power transition of a device in a power domain.
1359596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1360596ba34bSRafael J. Wysocki  *
1361596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1362596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1363596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1364596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1365596ba34bSRafael J. Wysocki  */
13669e9704eaSUlf Hansson static void genpd_complete(struct device *dev)
1367596ba34bSRafael J. Wysocki {
1368596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1369596ba34bSRafael J. Wysocki 
1370596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1371596ba34bSRafael J. Wysocki 
1372596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1373596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1374596ba34bSRafael J. Wysocki 		return;
1375596ba34bSRafael J. Wysocki 
13764d23a5e8SUlf Hansson 	pm_generic_complete(dev);
13774d23a5e8SUlf Hansson 
137835241d12SLina Iyer 	genpd_lock(genpd);
1379596ba34bSRafael J. Wysocki 
138039dd0f23SUlf Hansson 	genpd->prepared_count--;
13814d23a5e8SUlf Hansson 	if (!genpd->prepared_count)
13824d23a5e8SUlf Hansson 		genpd_queue_power_off_work(genpd);
1383596ba34bSRafael J. Wysocki 
138435241d12SLina Iyer 	genpd_unlock(genpd);
1385596ba34bSRafael J. Wysocki }
1386596ba34bSRafael J. Wysocki 
1387fc519890SUlf Hansson static void genpd_switch_state(struct device *dev, bool suspend)
138877f827deSRafael J. Wysocki {
138977f827deSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1390b9795a3eSUlf Hansson 	bool use_lock;
139177f827deSRafael J. Wysocki 
1392fe0c2baaSUlf Hansson 	genpd = dev_to_genpd_safe(dev);
1393fe0c2baaSUlf Hansson 	if (!genpd)
139477f827deSRafael J. Wysocki 		return;
139577f827deSRafael J. Wysocki 
1396b9795a3eSUlf Hansson 	use_lock = genpd_is_irq_safe(genpd);
1397b9795a3eSUlf Hansson 
1398b9795a3eSUlf Hansson 	if (use_lock)
1399b9795a3eSUlf Hansson 		genpd_lock(genpd);
1400b9795a3eSUlf Hansson 
140177f827deSRafael J. Wysocki 	if (suspend) {
140277f827deSRafael J. Wysocki 		genpd->suspended_count++;
1403b9795a3eSUlf Hansson 		genpd_sync_power_off(genpd, use_lock, 0);
140477f827deSRafael J. Wysocki 	} else {
1405b9795a3eSUlf Hansson 		genpd_sync_power_on(genpd, use_lock, 0);
140677f827deSRafael J. Wysocki 		genpd->suspended_count--;
140777f827deSRafael J. Wysocki 	}
1408b9795a3eSUlf Hansson 
1409b9795a3eSUlf Hansson 	if (use_lock)
1410b9795a3eSUlf Hansson 		genpd_unlock(genpd);
141177f827deSRafael J. Wysocki }
1412d47e6464SUlf Hansson 
1413fc519890SUlf Hansson /**
1414fc519890SUlf Hansson  * dev_pm_genpd_suspend - Synchronously try to suspend the genpd for @dev
1415fc519890SUlf Hansson  * @dev: The device that is attached to the genpd, that can be suspended.
1416fc519890SUlf Hansson  *
1417fc519890SUlf Hansson  * This routine should typically be called for a device that needs to be
1418b9795a3eSUlf Hansson  * suspended during the syscore suspend phase. It may also be called during
1419b9795a3eSUlf Hansson  * suspend-to-idle to suspend a corresponding CPU device that is attached to a
1420b9795a3eSUlf Hansson  * genpd.
1421fc519890SUlf Hansson  */
1422fc519890SUlf Hansson void dev_pm_genpd_suspend(struct device *dev)
1423d47e6464SUlf Hansson {
1424fc519890SUlf Hansson 	genpd_switch_state(dev, true);
1425d47e6464SUlf Hansson }
1426fc519890SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_suspend);
1427d47e6464SUlf Hansson 
1428fc519890SUlf Hansson /**
1429fc519890SUlf Hansson  * dev_pm_genpd_resume - Synchronously try to resume the genpd for @dev
1430fc519890SUlf Hansson  * @dev: The device that is attached to the genpd, which needs to be resumed.
1431fc519890SUlf Hansson  *
1432fc519890SUlf Hansson  * This routine should typically be called for a device that needs to be resumed
1433b9795a3eSUlf Hansson  * during the syscore resume phase. It may also be called during suspend-to-idle
1434b9795a3eSUlf Hansson  * to resume a corresponding CPU device that is attached to a genpd.
1435fc519890SUlf Hansson  */
1436fc519890SUlf Hansson void dev_pm_genpd_resume(struct device *dev)
1437d47e6464SUlf Hansson {
1438fc519890SUlf Hansson 	genpd_switch_state(dev, false);
1439d47e6464SUlf Hansson }
1440fc519890SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_resume);
144177f827deSRafael J. Wysocki 
1442d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
1443596ba34bSRafael J. Wysocki 
14449e9704eaSUlf Hansson #define genpd_prepare		NULL
14459e9704eaSUlf Hansson #define genpd_suspend_noirq	NULL
14469e9704eaSUlf Hansson #define genpd_resume_noirq	NULL
14479e9704eaSUlf Hansson #define genpd_freeze_noirq	NULL
14489e9704eaSUlf Hansson #define genpd_thaw_noirq	NULL
14499e9704eaSUlf Hansson #define genpd_poweroff_noirq	NULL
14509e9704eaSUlf Hansson #define genpd_restore_noirq	NULL
14519e9704eaSUlf Hansson #define genpd_complete		NULL
1452596ba34bSRafael J. Wysocki 
1453596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1454596ba34bSRafael J. Wysocki 
1455a174920dSUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev)
14561d5fcfecSRafael J. Wysocki {
14571d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
14583e235685SUlf Hansson 	int ret;
14593e235685SUlf Hansson 
14603e235685SUlf Hansson 	ret = dev_pm_get_subsys_data(dev);
14613e235685SUlf Hansson 	if (ret)
14623e235685SUlf Hansson 		return ERR_PTR(ret);
14631d5fcfecSRafael J. Wysocki 
14641d5fcfecSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
14653e235685SUlf Hansson 	if (!gpd_data) {
14663e235685SUlf Hansson 		ret = -ENOMEM;
14673e235685SUlf Hansson 		goto err_put;
14683e235685SUlf Hansson 	}
14691d5fcfecSRafael J. Wysocki 
1470f104e1e5SUlf Hansson 	gpd_data->base.dev = dev;
1471f104e1e5SUlf Hansson 	gpd_data->td.constraint_changed = true;
14720759e80bSRafael J. Wysocki 	gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
1473f104e1e5SUlf Hansson 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
147467e3242eSLina Iyer 	gpd_data->next_wakeup = KTIME_MAX;
1475f104e1e5SUlf Hansson 
1476f104e1e5SUlf Hansson 	spin_lock_irq(&dev->power.lock);
1477f104e1e5SUlf Hansson 
1478f104e1e5SUlf Hansson 	if (dev->power.subsys_data->domain_data) {
1479f104e1e5SUlf Hansson 		ret = -EINVAL;
1480f104e1e5SUlf Hansson 		goto err_free;
1481f104e1e5SUlf Hansson 	}
1482f104e1e5SUlf Hansson 
1483f104e1e5SUlf Hansson 	dev->power.subsys_data->domain_data = &gpd_data->base;
1484f104e1e5SUlf Hansson 
1485f104e1e5SUlf Hansson 	spin_unlock_irq(&dev->power.lock);
1486f104e1e5SUlf Hansson 
14871d5fcfecSRafael J. Wysocki 	return gpd_data;
14883e235685SUlf Hansson 
1489f104e1e5SUlf Hansson  err_free:
1490f104e1e5SUlf Hansson 	spin_unlock_irq(&dev->power.lock);
1491f104e1e5SUlf Hansson 	kfree(gpd_data);
14923e235685SUlf Hansson  err_put:
14933e235685SUlf Hansson 	dev_pm_put_subsys_data(dev);
14943e235685SUlf Hansson 	return ERR_PTR(ret);
14951d5fcfecSRafael J. Wysocki }
14961d5fcfecSRafael J. Wysocki 
149749d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev,
14981d5fcfecSRafael J. Wysocki 				struct generic_pm_domain_data *gpd_data)
14991d5fcfecSRafael J. Wysocki {
1500f104e1e5SUlf Hansson 	spin_lock_irq(&dev->power.lock);
1501f104e1e5SUlf Hansson 
1502f104e1e5SUlf Hansson 	dev->power.subsys_data->domain_data = NULL;
1503f104e1e5SUlf Hansson 
1504f104e1e5SUlf Hansson 	spin_unlock_irq(&dev->power.lock);
1505f104e1e5SUlf Hansson 
15061d5fcfecSRafael J. Wysocki 	kfree(gpd_data);
15073e235685SUlf Hansson 	dev_pm_put_subsys_data(dev);
15081d5fcfecSRafael J. Wysocki }
15091d5fcfecSRafael J. Wysocki 
1510b24e1965SUlf Hansson static void genpd_update_cpumask(struct generic_pm_domain *genpd,
1511eb594b73SUlf Hansson 				 int cpu, bool set, unsigned int depth)
1512eb594b73SUlf Hansson {
1513eb594b73SUlf Hansson 	struct gpd_link *link;
1514eb594b73SUlf Hansson 
1515eb594b73SUlf Hansson 	if (!genpd_is_cpu_domain(genpd))
1516eb594b73SUlf Hansson 		return;
1517eb594b73SUlf Hansson 
15188d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
15198d87ae48SKees Cook 		struct generic_pm_domain *parent = link->parent;
1520eb594b73SUlf Hansson 
15218d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
15228d87ae48SKees Cook 		genpd_update_cpumask(parent, cpu, set, depth + 1);
15238d87ae48SKees Cook 		genpd_unlock(parent);
1524eb594b73SUlf Hansson 	}
1525eb594b73SUlf Hansson 
1526eb594b73SUlf Hansson 	if (set)
1527eb594b73SUlf Hansson 		cpumask_set_cpu(cpu, genpd->cpus);
1528eb594b73SUlf Hansson 	else
1529eb594b73SUlf Hansson 		cpumask_clear_cpu(cpu, genpd->cpus);
1530eb594b73SUlf Hansson }
1531eb594b73SUlf Hansson 
1532b24e1965SUlf Hansson static void genpd_set_cpumask(struct generic_pm_domain *genpd, int cpu)
1533b24e1965SUlf Hansson {
1534b24e1965SUlf Hansson 	if (cpu >= 0)
1535b24e1965SUlf Hansson 		genpd_update_cpumask(genpd, cpu, true, 0);
1536b24e1965SUlf Hansson }
1537b24e1965SUlf Hansson 
1538b24e1965SUlf Hansson static void genpd_clear_cpumask(struct generic_pm_domain *genpd, int cpu)
1539b24e1965SUlf Hansson {
1540b24e1965SUlf Hansson 	if (cpu >= 0)
1541b24e1965SUlf Hansson 		genpd_update_cpumask(genpd, cpu, false, 0);
1542b24e1965SUlf Hansson }
1543b24e1965SUlf Hansson 
1544b24e1965SUlf Hansson static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev)
1545eb594b73SUlf Hansson {
1546eb594b73SUlf Hansson 	int cpu;
1547eb594b73SUlf Hansson 
1548eb594b73SUlf Hansson 	if (!genpd_is_cpu_domain(genpd))
1549b24e1965SUlf Hansson 		return -1;
1550eb594b73SUlf Hansson 
1551eb594b73SUlf Hansson 	for_each_possible_cpu(cpu) {
1552b24e1965SUlf Hansson 		if (get_cpu_device(cpu) == dev)
1553b24e1965SUlf Hansson 			return cpu;
1554eb594b73SUlf Hansson 	}
1555eb594b73SUlf Hansson 
1556b24e1965SUlf Hansson 	return -1;
1557eb594b73SUlf Hansson }
1558eb594b73SUlf Hansson 
1559f9ccd7c3SUlf Hansson static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1560f9ccd7c3SUlf Hansson 			    struct device *base_dev)
1561f721889fSRafael J. Wysocki {
1562c0356db7SUlf Hansson 	struct generic_pm_domain_data *gpd_data;
1563f9ccd7c3SUlf Hansson 	int ret;
1564f721889fSRafael J. Wysocki 
1565f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1566f721889fSRafael J. Wysocki 
1567f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1568f721889fSRafael J. Wysocki 		return -EINVAL;
1569f721889fSRafael J. Wysocki 
1570a174920dSUlf Hansson 	gpd_data = genpd_alloc_dev_data(dev);
15713e235685SUlf Hansson 	if (IS_ERR(gpd_data))
15723e235685SUlf Hansson 		return PTR_ERR(gpd_data);
15736ff7bb0dSRafael J. Wysocki 
1574f9ccd7c3SUlf Hansson 	gpd_data->cpu = genpd_get_cpu(genpd, base_dev);
1575b24e1965SUlf Hansson 
1576b472c2faSUlf Hansson 	ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0;
1577b472c2faSUlf Hansson 	if (ret)
1578b472c2faSUlf Hansson 		goto out;
1579d79b6fe1SGeert Uytterhoeven 
15802071ac98SJiada Wang 	genpd_lock(genpd);
15812071ac98SJiada Wang 
1582f9ccd7c3SUlf Hansson 	genpd_set_cpumask(genpd, gpd_data->cpu);
1583975e83cfSSudeep Holla 	dev_pm_domain_set(dev, &genpd->domain);
1584975e83cfSSudeep Holla 
158514b53064SUlf Hansson 	genpd->device_count++;
158614b53064SUlf Hansson 	genpd->max_off_time_changed = true;
158714b53064SUlf Hansson 
15881d5fcfecSRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
15896ff7bb0dSRafael J. Wysocki 
159035241d12SLina Iyer 	genpd_unlock(genpd);
15912071ac98SJiada Wang  out:
1592c0356db7SUlf Hansson 	if (ret)
1593c0356db7SUlf Hansson 		genpd_free_dev_data(dev, gpd_data);
1594c0356db7SUlf Hansson 	else
15950b07ee94SViresh Kumar 		dev_pm_qos_add_notifier(dev, &gpd_data->nb,
15960b07ee94SViresh Kumar 					DEV_PM_QOS_RESUME_LATENCY);
15971d5fcfecSRafael J. Wysocki 
1598f721889fSRafael J. Wysocki 	return ret;
1599f721889fSRafael J. Wysocki }
160019efa5ffSJon Hunter 
160119efa5ffSJon Hunter /**
16021a7a6707SUlf Hansson  * pm_genpd_add_device - Add a device to an I/O PM domain.
160319efa5ffSJon Hunter  * @genpd: PM domain to add the device to.
160419efa5ffSJon Hunter  * @dev: Device to be added.
160519efa5ffSJon Hunter  */
16061a7a6707SUlf Hansson int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
160719efa5ffSJon Hunter {
160819efa5ffSJon Hunter 	int ret;
160919efa5ffSJon Hunter 
161019efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
1611f9ccd7c3SUlf Hansson 	ret = genpd_add_device(genpd, dev, dev);
161219efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
161319efa5ffSJon Hunter 
161419efa5ffSJon Hunter 	return ret;
161519efa5ffSJon Hunter }
16161a7a6707SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_add_device);
1617f721889fSRafael J. Wysocki 
161885168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd,
1619f721889fSRafael J. Wysocki 			       struct device *dev)
1620f721889fSRafael J. Wysocki {
16216ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
16224605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1623f9ccd7c3SUlf Hansson 	int ret = 0;
1624f721889fSRafael J. Wysocki 
1625f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1626f721889fSRafael J. Wysocki 
1627c0356db7SUlf Hansson 	pdd = dev->power.subsys_data->domain_data;
1628c0356db7SUlf Hansson 	gpd_data = to_gpd_data(pdd);
16290b07ee94SViresh Kumar 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb,
16300b07ee94SViresh Kumar 				   DEV_PM_QOS_RESUME_LATENCY);
1631c0356db7SUlf Hansson 
163235241d12SLina Iyer 	genpd_lock(genpd);
1633f721889fSRafael J. Wysocki 
1634596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1635596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1636596ba34bSRafael J. Wysocki 		goto out;
1637596ba34bSRafael J. Wysocki 	}
1638596ba34bSRafael J. Wysocki 
16396ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
16406ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
16416ff7bb0dSRafael J. Wysocki 
1642f9ccd7c3SUlf Hansson 	genpd_clear_cpumask(genpd, gpd_data->cpu);
1643975e83cfSSudeep Holla 	dev_pm_domain_set(dev, NULL);
1644975e83cfSSudeep Holla 
1645efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
16466ff7bb0dSRafael J. Wysocki 
164735241d12SLina Iyer 	genpd_unlock(genpd);
16486ff7bb0dSRafael J. Wysocki 
16492071ac98SJiada Wang 	if (genpd->detach_dev)
16502071ac98SJiada Wang 		genpd->detach_dev(genpd, dev);
16512071ac98SJiada Wang 
165249d400c7SUlf Hansson 	genpd_free_dev_data(dev, gpd_data);
16531d5fcfecSRafael J. Wysocki 
16546ff7bb0dSRafael J. Wysocki 	return 0;
1655f721889fSRafael J. Wysocki 
1656596ba34bSRafael J. Wysocki  out:
165735241d12SLina Iyer 	genpd_unlock(genpd);
16580b07ee94SViresh Kumar 	dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY);
1659f721889fSRafael J. Wysocki 
1660f721889fSRafael J. Wysocki 	return ret;
1661f721889fSRafael J. Wysocki }
166285168d56SUlf Hansson 
166385168d56SUlf Hansson /**
166485168d56SUlf Hansson  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
166585168d56SUlf Hansson  * @dev: Device to be removed.
166685168d56SUlf Hansson  */
1667924f4486SUlf Hansson int pm_genpd_remove_device(struct device *dev)
166885168d56SUlf Hansson {
1669b3ad17c0SUlf Hansson 	struct generic_pm_domain *genpd = dev_to_genpd_safe(dev);
1670924f4486SUlf Hansson 
1671924f4486SUlf Hansson 	if (!genpd)
167285168d56SUlf Hansson 		return -EINVAL;
167385168d56SUlf Hansson 
167485168d56SUlf Hansson 	return genpd_remove_device(genpd, dev);
167585168d56SUlf Hansson }
167624c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
1677f721889fSRafael J. Wysocki 
1678d4f81383SUlf Hansson /**
1679d4f81383SUlf Hansson  * dev_pm_genpd_add_notifier - Add a genpd power on/off notifier for @dev
1680d4f81383SUlf Hansson  *
1681d4f81383SUlf Hansson  * @dev: Device that should be associated with the notifier
1682d4f81383SUlf Hansson  * @nb: The notifier block to register
1683d4f81383SUlf Hansson  *
1684d4f81383SUlf Hansson  * Users may call this function to add a genpd power on/off notifier for an
1685d4f81383SUlf Hansson  * attached @dev. Only one notifier per device is allowed. The notifier is
1686d4f81383SUlf Hansson  * sent when genpd is powering on/off the PM domain.
1687d4f81383SUlf Hansson  *
1688d4f81383SUlf Hansson  * It is assumed that the user guarantee that the genpd wouldn't be detached
1689d4f81383SUlf Hansson  * while this routine is getting called.
1690d4f81383SUlf Hansson  *
1691d4f81383SUlf Hansson  * Returns 0 on success and negative error values on failures.
1692d4f81383SUlf Hansson  */
1693d4f81383SUlf Hansson int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb)
1694d4f81383SUlf Hansson {
1695d4f81383SUlf Hansson 	struct generic_pm_domain *genpd;
1696d4f81383SUlf Hansson 	struct generic_pm_domain_data *gpd_data;
1697d4f81383SUlf Hansson 	int ret;
1698d4f81383SUlf Hansson 
1699d4f81383SUlf Hansson 	genpd = dev_to_genpd_safe(dev);
1700d4f81383SUlf Hansson 	if (!genpd)
1701d4f81383SUlf Hansson 		return -ENODEV;
1702d4f81383SUlf Hansson 
1703d4f81383SUlf Hansson 	if (WARN_ON(!dev->power.subsys_data ||
1704d4f81383SUlf Hansson 		     !dev->power.subsys_data->domain_data))
1705d4f81383SUlf Hansson 		return -EINVAL;
1706d4f81383SUlf Hansson 
1707d4f81383SUlf Hansson 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1708d4f81383SUlf Hansson 	if (gpd_data->power_nb)
1709d4f81383SUlf Hansson 		return -EEXIST;
1710d4f81383SUlf Hansson 
1711d4f81383SUlf Hansson 	genpd_lock(genpd);
1712d4f81383SUlf Hansson 	ret = raw_notifier_chain_register(&genpd->power_notifiers, nb);
1713d4f81383SUlf Hansson 	genpd_unlock(genpd);
1714d4f81383SUlf Hansson 
1715d4f81383SUlf Hansson 	if (ret) {
1716d4f81383SUlf Hansson 		dev_warn(dev, "failed to add notifier for PM domain %s\n",
1717d4f81383SUlf Hansson 			 genpd->name);
1718d4f81383SUlf Hansson 		return ret;
1719d4f81383SUlf Hansson 	}
1720d4f81383SUlf Hansson 
1721d4f81383SUlf Hansson 	gpd_data->power_nb = nb;
1722d4f81383SUlf Hansson 	return 0;
1723d4f81383SUlf Hansson }
1724d4f81383SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_add_notifier);
1725d4f81383SUlf Hansson 
1726d4f81383SUlf Hansson /**
1727d4f81383SUlf Hansson  * dev_pm_genpd_remove_notifier - Remove a genpd power on/off notifier for @dev
1728d4f81383SUlf Hansson  *
1729d4f81383SUlf Hansson  * @dev: Device that is associated with the notifier
1730d4f81383SUlf Hansson  *
1731d4f81383SUlf Hansson  * Users may call this function to remove a genpd power on/off notifier for an
1732d4f81383SUlf Hansson  * attached @dev.
1733d4f81383SUlf Hansson  *
1734d4f81383SUlf Hansson  * It is assumed that the user guarantee that the genpd wouldn't be detached
1735d4f81383SUlf Hansson  * while this routine is getting called.
1736d4f81383SUlf Hansson  *
1737d4f81383SUlf Hansson  * Returns 0 on success and negative error values on failures.
1738d4f81383SUlf Hansson  */
1739d4f81383SUlf Hansson int dev_pm_genpd_remove_notifier(struct device *dev)
1740d4f81383SUlf Hansson {
1741d4f81383SUlf Hansson 	struct generic_pm_domain *genpd;
1742d4f81383SUlf Hansson 	struct generic_pm_domain_data *gpd_data;
1743d4f81383SUlf Hansson 	int ret;
1744d4f81383SUlf Hansson 
1745d4f81383SUlf Hansson 	genpd = dev_to_genpd_safe(dev);
1746d4f81383SUlf Hansson 	if (!genpd)
1747d4f81383SUlf Hansson 		return -ENODEV;
1748d4f81383SUlf Hansson 
1749d4f81383SUlf Hansson 	if (WARN_ON(!dev->power.subsys_data ||
1750d4f81383SUlf Hansson 		     !dev->power.subsys_data->domain_data))
1751d4f81383SUlf Hansson 		return -EINVAL;
1752d4f81383SUlf Hansson 
1753d4f81383SUlf Hansson 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1754d4f81383SUlf Hansson 	if (!gpd_data->power_nb)
1755d4f81383SUlf Hansson 		return -ENODEV;
1756d4f81383SUlf Hansson 
1757d4f81383SUlf Hansson 	genpd_lock(genpd);
1758d4f81383SUlf Hansson 	ret = raw_notifier_chain_unregister(&genpd->power_notifiers,
1759d4f81383SUlf Hansson 					    gpd_data->power_nb);
1760d4f81383SUlf Hansson 	genpd_unlock(genpd);
1761d4f81383SUlf Hansson 
1762d4f81383SUlf Hansson 	if (ret) {
1763d4f81383SUlf Hansson 		dev_warn(dev, "failed to remove notifier for PM domain %s\n",
1764d4f81383SUlf Hansson 			 genpd->name);
1765d4f81383SUlf Hansson 		return ret;
1766d4f81383SUlf Hansson 	}
1767d4f81383SUlf Hansson 
1768d4f81383SUlf Hansson 	gpd_data->power_nb = NULL;
1769d4f81383SUlf Hansson 	return 0;
1770d4f81383SUlf Hansson }
1771d4f81383SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_remove_notifier);
1772d4f81383SUlf Hansson 
177319efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd,
1774bc0403ffSRafael J. Wysocki 			       struct generic_pm_domain *subdomain)
1775f721889fSRafael J. Wysocki {
17762547923dSLina Iyer 	struct gpd_link *link, *itr;
1777f721889fSRafael J. Wysocki 	int ret = 0;
1778f721889fSRafael J. Wysocki 
1779fb7268beSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)
1780fb7268beSRafael J. Wysocki 	    || genpd == subdomain)
1781f721889fSRafael J. Wysocki 		return -EINVAL;
1782f721889fSRafael J. Wysocki 
1783d716f479SLina Iyer 	/*
1784d716f479SLina Iyer 	 * If the domain can be powered on/off in an IRQ safe
1785d716f479SLina Iyer 	 * context, ensure that the subdomain can also be
1786d716f479SLina Iyer 	 * powered on/off in that context.
1787d716f479SLina Iyer 	 */
1788d716f479SLina Iyer 	if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) {
178944cae7d5SDan Carpenter 		WARN(1, "Parent %s of subdomain %s must be IRQ safe\n",
1790d716f479SLina Iyer 				genpd->name, subdomain->name);
1791d716f479SLina Iyer 		return -EINVAL;
1792d716f479SLina Iyer 	}
1793d716f479SLina Iyer 
17942547923dSLina Iyer 	link = kzalloc(sizeof(*link), GFP_KERNEL);
17952547923dSLina Iyer 	if (!link)
17962547923dSLina Iyer 		return -ENOMEM;
17972547923dSLina Iyer 
179835241d12SLina Iyer 	genpd_lock(subdomain);
179935241d12SLina Iyer 	genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
1800f721889fSRafael J. Wysocki 
180141e2c8e0SUlf Hansson 	if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) {
1802f721889fSRafael J. Wysocki 		ret = -EINVAL;
1803f721889fSRafael J. Wysocki 		goto out;
1804f721889fSRafael J. Wysocki 	}
1805f721889fSRafael J. Wysocki 
18068d87ae48SKees Cook 	list_for_each_entry(itr, &genpd->parent_links, parent_node) {
18078d87ae48SKees Cook 		if (itr->child == subdomain && itr->parent == genpd) {
1808f721889fSRafael J. Wysocki 			ret = -EINVAL;
1809f721889fSRafael J. Wysocki 			goto out;
1810f721889fSRafael J. Wysocki 		}
1811f721889fSRafael J. Wysocki 	}
1812f721889fSRafael J. Wysocki 
18138d87ae48SKees Cook 	link->parent = genpd;
18148d87ae48SKees Cook 	list_add_tail(&link->parent_node, &genpd->parent_links);
18158d87ae48SKees Cook 	link->child = subdomain;
18168d87ae48SKees Cook 	list_add_tail(&link->child_node, &subdomain->child_links);
181741e2c8e0SUlf Hansson 	if (genpd_status_on(subdomain))
1818c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1819f721889fSRafael J. Wysocki 
1820f721889fSRafael J. Wysocki  out:
182135241d12SLina Iyer 	genpd_unlock(genpd);
182235241d12SLina Iyer 	genpd_unlock(subdomain);
18232547923dSLina Iyer 	if (ret)
18242547923dSLina Iyer 		kfree(link);
1825f721889fSRafael J. Wysocki 	return ret;
1826f721889fSRafael J. Wysocki }
182719efa5ffSJon Hunter 
182819efa5ffSJon Hunter /**
182919efa5ffSJon Hunter  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
18308d87ae48SKees Cook  * @genpd: Leader PM domain to add the subdomain to.
183119efa5ffSJon Hunter  * @subdomain: Subdomain to be added.
183219efa5ffSJon Hunter  */
183319efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
183419efa5ffSJon Hunter 			   struct generic_pm_domain *subdomain)
183519efa5ffSJon Hunter {
183619efa5ffSJon Hunter 	int ret;
183719efa5ffSJon Hunter 
183819efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
183919efa5ffSJon Hunter 	ret = genpd_add_subdomain(genpd, subdomain);
184019efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
184119efa5ffSJon Hunter 
184219efa5ffSJon Hunter 	return ret;
184319efa5ffSJon Hunter }
1844d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
1845f721889fSRafael J. Wysocki 
1846f721889fSRafael J. Wysocki /**
1847f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
18488d87ae48SKees Cook  * @genpd: Leader PM domain to remove the subdomain from.
18495063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1850f721889fSRafael J. Wysocki  */
1851f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
18525063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1853f721889fSRafael J. Wysocki {
1854c6e83cacSKrzysztof Kozlowski 	struct gpd_link *l, *link;
1855f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1856f721889fSRafael J. Wysocki 
18575063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1858f721889fSRafael J. Wysocki 		return -EINVAL;
1859f721889fSRafael J. Wysocki 
186035241d12SLina Iyer 	genpd_lock(subdomain);
186135241d12SLina Iyer 	genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
1862f721889fSRafael J. Wysocki 
18638d87ae48SKees Cook 	if (!list_empty(&subdomain->parent_links) || subdomain->device_count) {
18647a5bd127SJoe Perches 		pr_warn("%s: unable to remove subdomain %s\n",
18657a5bd127SJoe Perches 			genpd->name, subdomain->name);
186630e7a65bSJon Hunter 		ret = -EBUSY;
186730e7a65bSJon Hunter 		goto out;
186830e7a65bSJon Hunter 	}
186930e7a65bSJon Hunter 
18708d87ae48SKees Cook 	list_for_each_entry_safe(link, l, &genpd->parent_links, parent_node) {
18718d87ae48SKees Cook 		if (link->child != subdomain)
1872f721889fSRafael J. Wysocki 			continue;
1873f721889fSRafael J. Wysocki 
18748d87ae48SKees Cook 		list_del(&link->parent_node);
18758d87ae48SKees Cook 		list_del(&link->child_node);
18765063ce15SRafael J. Wysocki 		kfree(link);
187741e2c8e0SUlf Hansson 		if (genpd_status_on(subdomain))
1878f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1879f721889fSRafael J. Wysocki 
1880f721889fSRafael J. Wysocki 		ret = 0;
1881f721889fSRafael J. Wysocki 		break;
1882f721889fSRafael J. Wysocki 	}
1883f721889fSRafael J. Wysocki 
188430e7a65bSJon Hunter out:
188535241d12SLina Iyer 	genpd_unlock(genpd);
188635241d12SLina Iyer 	genpd_unlock(subdomain);
1887f721889fSRafael J. Wysocki 
1888f721889fSRafael J. Wysocki 	return ret;
1889f721889fSRafael J. Wysocki }
1890d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
1891f721889fSRafael J. Wysocki 
189249a27e27SUlf Hansson static void genpd_free_default_power_state(struct genpd_power_state *states,
189349a27e27SUlf Hansson 					   unsigned int state_count)
189449a27e27SUlf Hansson {
189549a27e27SUlf Hansson 	kfree(states);
189649a27e27SUlf Hansson }
189749a27e27SUlf Hansson 
189859d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
189959d65b73SLina Iyer {
190059d65b73SLina Iyer 	struct genpd_power_state *state;
190159d65b73SLina Iyer 
190259d65b73SLina Iyer 	state = kzalloc(sizeof(*state), GFP_KERNEL);
190359d65b73SLina Iyer 	if (!state)
190459d65b73SLina Iyer 		return -ENOMEM;
190559d65b73SLina Iyer 
190659d65b73SLina Iyer 	genpd->states = state;
190759d65b73SLina Iyer 	genpd->state_count = 1;
190849a27e27SUlf Hansson 	genpd->free_states = genpd_free_default_power_state;
190959d65b73SLina Iyer 
191059d65b73SLina Iyer 	return 0;
191159d65b73SLina Iyer }
191259d65b73SLina Iyer 
1913d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd)
1914d716f479SLina Iyer {
1915d716f479SLina Iyer 	if (genpd->flags & GENPD_FLAG_IRQ_SAFE) {
1916d716f479SLina Iyer 		spin_lock_init(&genpd->slock);
1917d716f479SLina Iyer 		genpd->lock_ops = &genpd_spin_ops;
1918d716f479SLina Iyer 	} else {
1919d716f479SLina Iyer 		mutex_init(&genpd->mlock);
1920d716f479SLina Iyer 		genpd->lock_ops = &genpd_mtx_ops;
1921d716f479SLina Iyer 	}
1922d716f479SLina Iyer }
1923d716f479SLina Iyer 
1924d23b9b00SRafael J. Wysocki /**
1925f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
1926f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
1927f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
1928f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
19297eb231c3SUlf Hansson  *
19307eb231c3SUlf Hansson  * Returns 0 on successful initialization, else a negative error code.
1931f721889fSRafael J. Wysocki  */
19327eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd,
1933f721889fSRafael J. Wysocki 		  struct dev_power_governor *gov, bool is_off)
1934f721889fSRafael J. Wysocki {
193559d65b73SLina Iyer 	int ret;
193659d65b73SLina Iyer 
1937f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
19387eb231c3SUlf Hansson 		return -EINVAL;
1939f721889fSRafael J. Wysocki 
19408d87ae48SKees Cook 	INIT_LIST_HEAD(&genpd->parent_links);
19418d87ae48SKees Cook 	INIT_LIST_HEAD(&genpd->child_links);
1942f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1943d4f81383SUlf Hansson 	RAW_INIT_NOTIFIER_HEAD(&genpd->power_notifiers);
1944d716f479SLina Iyer 	genpd_lock_init(genpd);
1945f721889fSRafael J. Wysocki 	genpd->gov = gov;
1946f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1947c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
194849f618e1SUlf Hansson 	genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
1949596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1950221e9b58SRafael J. Wysocki 	genpd->max_off_time_ns = -1;
19516ff7bb0dSRafael J. Wysocki 	genpd->max_off_time_changed = true;
1952de0aa06dSJon Hunter 	genpd->provider = NULL;
1953de0aa06dSJon Hunter 	genpd->has_provider = false;
1954afece3abSThara Gopinath 	genpd->accounting_time = ktime_get();
1955795bd2e7SUlf Hansson 	genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
1956795bd2e7SUlf Hansson 	genpd->domain.ops.runtime_resume = genpd_runtime_resume;
19579e9704eaSUlf Hansson 	genpd->domain.ops.prepare = genpd_prepare;
19589e9704eaSUlf Hansson 	genpd->domain.ops.suspend_noirq = genpd_suspend_noirq;
19599e9704eaSUlf Hansson 	genpd->domain.ops.resume_noirq = genpd_resume_noirq;
19609e9704eaSUlf Hansson 	genpd->domain.ops.freeze_noirq = genpd_freeze_noirq;
19619e9704eaSUlf Hansson 	genpd->domain.ops.thaw_noirq = genpd_thaw_noirq;
19629e9704eaSUlf Hansson 	genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq;
19639e9704eaSUlf Hansson 	genpd->domain.ops.restore_noirq = genpd_restore_noirq;
19649e9704eaSUlf Hansson 	genpd->domain.ops.complete = genpd_complete;
1965ea71c596SUlf Hansson 	genpd->domain.start = genpd_dev_pm_start;
1966c11f6f5bSUlf Hansson 
1967c11f6f5bSUlf Hansson 	if (genpd->flags & GENPD_FLAG_PM_CLK) {
1968c11f6f5bSUlf Hansson 		genpd->dev_ops.stop = pm_clk_suspend;
1969c11f6f5bSUlf Hansson 		genpd->dev_ops.start = pm_clk_resume;
1970c11f6f5bSUlf Hansson 	}
1971c11f6f5bSUlf Hansson 
1972ffaa42e8SUlf Hansson 	/* Always-on domains must be powered on at initialization. */
1973ed61e18aSLeonard Crestez 	if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) &&
1974ed61e18aSLeonard Crestez 			!genpd_status_on(genpd))
1975ffaa42e8SUlf Hansson 		return -EINVAL;
1976ffaa42e8SUlf Hansson 
1977eb594b73SUlf Hansson 	if (genpd_is_cpu_domain(genpd) &&
1978eb594b73SUlf Hansson 	    !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL))
1979eb594b73SUlf Hansson 		return -ENOMEM;
1980eb594b73SUlf Hansson 
1981fc5cbf0cSAxel Haslam 	/* Use only one "off" state if there were no states declared */
198259d65b73SLina Iyer 	if (genpd->state_count == 0) {
198359d65b73SLina Iyer 		ret = genpd_set_default_power_state(genpd);
1984eb594b73SUlf Hansson 		if (ret) {
1985eb594b73SUlf Hansson 			if (genpd_is_cpu_domain(genpd))
1986eb594b73SUlf Hansson 				free_cpumask_var(genpd->cpus);
198759d65b73SLina Iyer 			return ret;
1988eb594b73SUlf Hansson 		}
198946b7fe94SAisheng Dong 	} else if (!gov && genpd->state_count > 1) {
19902c9b7f87SUlf Hansson 		pr_warn("%s: no governor for states\n", genpd->name);
199159d65b73SLina Iyer 	}
1992fc5cbf0cSAxel Haslam 
1993401ea157SViresh Kumar 	device_initialize(&genpd->dev);
1994401ea157SViresh Kumar 	dev_set_name(&genpd->dev, "%s", genpd->name);
1995401ea157SViresh Kumar 
19965125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
19975125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
1998718072ceSThierry Strudel 	genpd_debug_add(genpd);
19995125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
20007eb231c3SUlf Hansson 
20017eb231c3SUlf Hansson 	return 0;
20025125bbf3SRafael J. Wysocki }
2003be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init);
2004aa42240aSTomasz Figa 
20053fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd)
20063fe57710SJon Hunter {
20073fe57710SJon Hunter 	struct gpd_link *l, *link;
20083fe57710SJon Hunter 
20093fe57710SJon Hunter 	if (IS_ERR_OR_NULL(genpd))
20103fe57710SJon Hunter 		return -EINVAL;
20113fe57710SJon Hunter 
201235241d12SLina Iyer 	genpd_lock(genpd);
20133fe57710SJon Hunter 
20143fe57710SJon Hunter 	if (genpd->has_provider) {
201535241d12SLina Iyer 		genpd_unlock(genpd);
20163fe57710SJon Hunter 		pr_err("Provider present, unable to remove %s\n", genpd->name);
20173fe57710SJon Hunter 		return -EBUSY;
20183fe57710SJon Hunter 	}
20193fe57710SJon Hunter 
20208d87ae48SKees Cook 	if (!list_empty(&genpd->parent_links) || genpd->device_count) {
202135241d12SLina Iyer 		genpd_unlock(genpd);
20223fe57710SJon Hunter 		pr_err("%s: unable to remove %s\n", __func__, genpd->name);
20233fe57710SJon Hunter 		return -EBUSY;
20243fe57710SJon Hunter 	}
20253fe57710SJon Hunter 
20268d87ae48SKees Cook 	list_for_each_entry_safe(link, l, &genpd->child_links, child_node) {
20278d87ae48SKees Cook 		list_del(&link->parent_node);
20288d87ae48SKees Cook 		list_del(&link->child_node);
20293fe57710SJon Hunter 		kfree(link);
20303fe57710SJon Hunter 	}
20313fe57710SJon Hunter 
2032718072ceSThierry Strudel 	genpd_debug_remove(genpd);
20333fe57710SJon Hunter 	list_del(&genpd->gpd_list_node);
203435241d12SLina Iyer 	genpd_unlock(genpd);
20353fe57710SJon Hunter 	cancel_work_sync(&genpd->power_off_work);
2036eb594b73SUlf Hansson 	if (genpd_is_cpu_domain(genpd))
2037eb594b73SUlf Hansson 		free_cpumask_var(genpd->cpus);
203849a27e27SUlf Hansson 	if (genpd->free_states)
203949a27e27SUlf Hansson 		genpd->free_states(genpd->states, genpd->state_count);
204049a27e27SUlf Hansson 
20413fe57710SJon Hunter 	pr_debug("%s: removed %s\n", __func__, genpd->name);
20423fe57710SJon Hunter 
20433fe57710SJon Hunter 	return 0;
20443fe57710SJon Hunter }
20453fe57710SJon Hunter 
20463fe57710SJon Hunter /**
20473fe57710SJon Hunter  * pm_genpd_remove - Remove a generic I/O PM domain
20483fe57710SJon Hunter  * @genpd: Pointer to PM domain that is to be removed.
20493fe57710SJon Hunter  *
20503fe57710SJon Hunter  * To remove the PM domain, this function:
20513fe57710SJon Hunter  *  - Removes the PM domain as a subdomain to any parent domains,
20523fe57710SJon Hunter  *    if it was added.
20533fe57710SJon Hunter  *  - Removes the PM domain from the list of registered PM domains.
20543fe57710SJon Hunter  *
20553fe57710SJon Hunter  * The PM domain will only be removed, if the associated provider has
20563fe57710SJon Hunter  * been removed, it is not a parent to any other PM domain and has no
20573fe57710SJon Hunter  * devices associated with it.
20583fe57710SJon Hunter  */
20593fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd)
20603fe57710SJon Hunter {
20613fe57710SJon Hunter 	int ret;
20623fe57710SJon Hunter 
20633fe57710SJon Hunter 	mutex_lock(&gpd_list_lock);
20643fe57710SJon Hunter 	ret = genpd_remove(genpd);
20653fe57710SJon Hunter 	mutex_unlock(&gpd_list_lock);
20663fe57710SJon Hunter 
20673fe57710SJon Hunter 	return ret;
20683fe57710SJon Hunter }
20693fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove);
20703fe57710SJon Hunter 
2071aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
2072892ebdccSJon Hunter 
2073aa42240aSTomasz Figa /*
2074aa42240aSTomasz Figa  * Device Tree based PM domain providers.
2075aa42240aSTomasz Figa  *
2076aa42240aSTomasz Figa  * The code below implements generic device tree based PM domain providers that
2077aa42240aSTomasz Figa  * bind device tree nodes with generic PM domains registered in the system.
2078aa42240aSTomasz Figa  *
2079aa42240aSTomasz Figa  * Any driver that registers generic PM domains and needs to support binding of
2080aa42240aSTomasz Figa  * devices to these domains is supposed to register a PM domain provider, which
2081aa42240aSTomasz Figa  * maps a PM domain specifier retrieved from the device tree to a PM domain.
2082aa42240aSTomasz Figa  *
2083aa42240aSTomasz Figa  * Two simple mapping functions have been provided for convenience:
2084892ebdccSJon Hunter  *  - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
2085892ebdccSJon Hunter  *  - genpd_xlate_onecell() for mapping of multiple PM domains per node by
2086aa42240aSTomasz Figa  *    index.
2087aa42240aSTomasz Figa  */
2088aa42240aSTomasz Figa 
2089aa42240aSTomasz Figa /**
2090aa42240aSTomasz Figa  * struct of_genpd_provider - PM domain provider registration structure
2091aa42240aSTomasz Figa  * @link: Entry in global list of PM domain providers
2092aa42240aSTomasz Figa  * @node: Pointer to device tree node of PM domain provider
2093aa42240aSTomasz Figa  * @xlate: Provider-specific xlate callback mapping a set of specifier cells
2094aa42240aSTomasz Figa  *         into a PM domain.
2095aa42240aSTomasz Figa  * @data: context pointer to be passed into @xlate callback
2096aa42240aSTomasz Figa  */
2097aa42240aSTomasz Figa struct of_genpd_provider {
2098aa42240aSTomasz Figa 	struct list_head link;
2099aa42240aSTomasz Figa 	struct device_node *node;
2100aa42240aSTomasz Figa 	genpd_xlate_t xlate;
2101aa42240aSTomasz Figa 	void *data;
2102aa42240aSTomasz Figa };
2103aa42240aSTomasz Figa 
2104aa42240aSTomasz Figa /* List of registered PM domain providers. */
2105aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers);
2106aa42240aSTomasz Figa /* Mutex to protect the list above. */
2107aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex);
2108aa42240aSTomasz Figa 
2109aa42240aSTomasz Figa /**
2110892ebdccSJon Hunter  * genpd_xlate_simple() - Xlate function for direct node-domain mapping
2111aa42240aSTomasz Figa  * @genpdspec: OF phandle args to map into a PM domain
2112aa42240aSTomasz Figa  * @data: xlate function private data - pointer to struct generic_pm_domain
2113aa42240aSTomasz Figa  *
2114aa42240aSTomasz Figa  * This is a generic xlate function that can be used to model PM domains that
2115aa42240aSTomasz Figa  * have their own device tree nodes. The private data of xlate function needs
2116aa42240aSTomasz Figa  * to be a valid pointer to struct generic_pm_domain.
2117aa42240aSTomasz Figa  */
2118892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple(
2119aa42240aSTomasz Figa 					struct of_phandle_args *genpdspec,
2120aa42240aSTomasz Figa 					void *data)
2121aa42240aSTomasz Figa {
2122aa42240aSTomasz Figa 	return data;
2123aa42240aSTomasz Figa }
2124aa42240aSTomasz Figa 
2125aa42240aSTomasz Figa /**
2126892ebdccSJon Hunter  * genpd_xlate_onecell() - Xlate function using a single index.
2127aa42240aSTomasz Figa  * @genpdspec: OF phandle args to map into a PM domain
2128aa42240aSTomasz Figa  * @data: xlate function private data - pointer to struct genpd_onecell_data
2129aa42240aSTomasz Figa  *
2130aa42240aSTomasz Figa  * This is a generic xlate function that can be used to model simple PM domain
2131aa42240aSTomasz Figa  * controllers that have one device tree node and provide multiple PM domains.
2132aa42240aSTomasz Figa  * A single cell is used as an index into an array of PM domains specified in
2133aa42240aSTomasz Figa  * the genpd_onecell_data struct when registering the provider.
2134aa42240aSTomasz Figa  */
2135892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell(
2136aa42240aSTomasz Figa 					struct of_phandle_args *genpdspec,
2137aa42240aSTomasz Figa 					void *data)
2138aa42240aSTomasz Figa {
2139aa42240aSTomasz Figa 	struct genpd_onecell_data *genpd_data = data;
2140aa42240aSTomasz Figa 	unsigned int idx = genpdspec->args[0];
2141aa42240aSTomasz Figa 
2142aa42240aSTomasz Figa 	if (genpdspec->args_count != 1)
2143aa42240aSTomasz Figa 		return ERR_PTR(-EINVAL);
2144aa42240aSTomasz Figa 
2145aa42240aSTomasz Figa 	if (idx >= genpd_data->num_domains) {
2146aa42240aSTomasz Figa 		pr_err("%s: invalid domain index %u\n", __func__, idx);
2147aa42240aSTomasz Figa 		return ERR_PTR(-EINVAL);
2148aa42240aSTomasz Figa 	}
2149aa42240aSTomasz Figa 
2150aa42240aSTomasz Figa 	if (!genpd_data->domains[idx])
2151aa42240aSTomasz Figa 		return ERR_PTR(-ENOENT);
2152aa42240aSTomasz Figa 
2153aa42240aSTomasz Figa 	return genpd_data->domains[idx];
2154aa42240aSTomasz Figa }
2155aa42240aSTomasz Figa 
2156aa42240aSTomasz Figa /**
2157892ebdccSJon Hunter  * genpd_add_provider() - Register a PM domain provider for a node
2158aa42240aSTomasz Figa  * @np: Device node pointer associated with the PM domain provider.
2159aa42240aSTomasz Figa  * @xlate: Callback for decoding PM domain from phandle arguments.
2160aa42240aSTomasz Figa  * @data: Context pointer for @xlate callback.
2161aa42240aSTomasz Figa  */
2162892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
2163aa42240aSTomasz Figa 			      void *data)
2164aa42240aSTomasz Figa {
2165aa42240aSTomasz Figa 	struct of_genpd_provider *cp;
2166aa42240aSTomasz Figa 
2167aa42240aSTomasz Figa 	cp = kzalloc(sizeof(*cp), GFP_KERNEL);
2168aa42240aSTomasz Figa 	if (!cp)
2169aa42240aSTomasz Figa 		return -ENOMEM;
2170aa42240aSTomasz Figa 
2171aa42240aSTomasz Figa 	cp->node = of_node_get(np);
2172aa42240aSTomasz Figa 	cp->data = data;
2173aa42240aSTomasz Figa 	cp->xlate = xlate;
2174bab2d712SSaravana Kannan 	fwnode_dev_initialized(&np->fwnode, true);
2175aa42240aSTomasz Figa 
2176aa42240aSTomasz Figa 	mutex_lock(&of_genpd_mutex);
2177aa42240aSTomasz Figa 	list_add(&cp->link, &of_genpd_providers);
2178aa42240aSTomasz Figa 	mutex_unlock(&of_genpd_mutex);
2179ea11e94bSRob Herring 	pr_debug("Added domain provider from %pOF\n", np);
2180aa42240aSTomasz Figa 
2181aa42240aSTomasz Figa 	return 0;
2182aa42240aSTomasz Figa }
2183892ebdccSJon Hunter 
2184fe0c2baaSUlf Hansson static bool genpd_present(const struct generic_pm_domain *genpd)
2185fe0c2baaSUlf Hansson {
2186fe0c2baaSUlf Hansson 	const struct generic_pm_domain *gpd;
2187fe0c2baaSUlf Hansson 
2188fe0c2baaSUlf Hansson 	list_for_each_entry(gpd, &gpd_list, gpd_list_node)
2189fe0c2baaSUlf Hansson 		if (gpd == genpd)
2190fe0c2baaSUlf Hansson 			return true;
2191fe0c2baaSUlf Hansson 	return false;
2192fe0c2baaSUlf Hansson }
2193fe0c2baaSUlf Hansson 
2194892ebdccSJon Hunter /**
2195892ebdccSJon Hunter  * of_genpd_add_provider_simple() - Register a simple PM domain provider
2196892ebdccSJon Hunter  * @np: Device node pointer associated with the PM domain provider.
2197892ebdccSJon Hunter  * @genpd: Pointer to PM domain associated with the PM domain provider.
2198892ebdccSJon Hunter  */
2199892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np,
2200892ebdccSJon Hunter 				 struct generic_pm_domain *genpd)
2201892ebdccSJon Hunter {
22020159ec67SJon Hunter 	int ret = -EINVAL;
22030159ec67SJon Hunter 
22040159ec67SJon Hunter 	if (!np || !genpd)
22050159ec67SJon Hunter 		return -EINVAL;
22060159ec67SJon Hunter 
22070159ec67SJon Hunter 	mutex_lock(&gpd_list_lock);
22080159ec67SJon Hunter 
22096a0ae73dSViresh Kumar 	if (!genpd_present(genpd))
22106a0ae73dSViresh Kumar 		goto unlock;
22116a0ae73dSViresh Kumar 
22126a0ae73dSViresh Kumar 	genpd->dev.of_node = np;
22136a0ae73dSViresh Kumar 
22146a0ae73dSViresh Kumar 	/* Parse genpd OPP table */
22156a0ae73dSViresh Kumar 	if (genpd->set_performance_state) {
22166a0ae73dSViresh Kumar 		ret = dev_pm_opp_of_add_table(&genpd->dev);
22176a0ae73dSViresh Kumar 		if (ret) {
2218dd461cd9SStephan Gerhold 			if (ret != -EPROBE_DEFER)
22196a0ae73dSViresh Kumar 				dev_err(&genpd->dev, "Failed to add OPP table: %d\n",
22206a0ae73dSViresh Kumar 					ret);
22216a0ae73dSViresh Kumar 			goto unlock;
2222de0aa06dSJon Hunter 		}
22231067ae3eSViresh Kumar 
22241067ae3eSViresh Kumar 		/*
22251067ae3eSViresh Kumar 		 * Save table for faster processing while setting performance
22261067ae3eSViresh Kumar 		 * state.
22271067ae3eSViresh Kumar 		 */
22281067ae3eSViresh Kumar 		genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
2229dd461cd9SStephan Gerhold 		WARN_ON(IS_ERR(genpd->opp_table));
22308ce95844SViresh Kumar 	}
2231de0aa06dSJon Hunter 
22326a0ae73dSViresh Kumar 	ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
22336a0ae73dSViresh Kumar 	if (ret) {
22341067ae3eSViresh Kumar 		if (genpd->set_performance_state) {
22351067ae3eSViresh Kumar 			dev_pm_opp_put_opp_table(genpd->opp_table);
22366a0ae73dSViresh Kumar 			dev_pm_opp_of_remove_table(&genpd->dev);
22371067ae3eSViresh Kumar 		}
22386a0ae73dSViresh Kumar 
22396a0ae73dSViresh Kumar 		goto unlock;
22406a0ae73dSViresh Kumar 	}
22416a0ae73dSViresh Kumar 
22426a0ae73dSViresh Kumar 	genpd->provider = &np->fwnode;
22436a0ae73dSViresh Kumar 	genpd->has_provider = true;
22446a0ae73dSViresh Kumar 
22456a0ae73dSViresh Kumar unlock:
22460159ec67SJon Hunter 	mutex_unlock(&gpd_list_lock);
22470159ec67SJon Hunter 
22480159ec67SJon Hunter 	return ret;
2249892ebdccSJon Hunter }
2250892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
2251892ebdccSJon Hunter 
2252892ebdccSJon Hunter /**
2253892ebdccSJon Hunter  * of_genpd_add_provider_onecell() - Register a onecell PM domain provider
2254892ebdccSJon Hunter  * @np: Device node pointer associated with the PM domain provider.
2255892ebdccSJon Hunter  * @data: Pointer to the data associated with the PM domain provider.
2256892ebdccSJon Hunter  */
2257892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np,
2258892ebdccSJon Hunter 				  struct genpd_onecell_data *data)
2259892ebdccSJon Hunter {
22606a0ae73dSViresh Kumar 	struct generic_pm_domain *genpd;
22610159ec67SJon Hunter 	unsigned int i;
2262de0aa06dSJon Hunter 	int ret = -EINVAL;
22630159ec67SJon Hunter 
22640159ec67SJon Hunter 	if (!np || !data)
22650159ec67SJon Hunter 		return -EINVAL;
22660159ec67SJon Hunter 
22670159ec67SJon Hunter 	mutex_lock(&gpd_list_lock);
22680159ec67SJon Hunter 
226940845524SThierry Reding 	if (!data->xlate)
227040845524SThierry Reding 		data->xlate = genpd_xlate_onecell;
227140845524SThierry Reding 
22720159ec67SJon Hunter 	for (i = 0; i < data->num_domains; i++) {
22736a0ae73dSViresh Kumar 		genpd = data->domains[i];
22746a0ae73dSViresh Kumar 
22756a0ae73dSViresh Kumar 		if (!genpd)
2276609bed67STomeu Vizoso 			continue;
22776a0ae73dSViresh Kumar 		if (!genpd_present(genpd))
2278de0aa06dSJon Hunter 			goto error;
2279de0aa06dSJon Hunter 
22806a0ae73dSViresh Kumar 		genpd->dev.of_node = np;
22816a0ae73dSViresh Kumar 
22826a0ae73dSViresh Kumar 		/* Parse genpd OPP table */
22836a0ae73dSViresh Kumar 		if (genpd->set_performance_state) {
22846a0ae73dSViresh Kumar 			ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
22856a0ae73dSViresh Kumar 			if (ret) {
2286dd461cd9SStephan Gerhold 				if (ret != -EPROBE_DEFER)
22876a0ae73dSViresh Kumar 					dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n",
22886a0ae73dSViresh Kumar 						i, ret);
22896a0ae73dSViresh Kumar 				goto error;
22906a0ae73dSViresh Kumar 			}
22911067ae3eSViresh Kumar 
22921067ae3eSViresh Kumar 			/*
22931067ae3eSViresh Kumar 			 * Save table for faster processing while setting
22941067ae3eSViresh Kumar 			 * performance state.
22951067ae3eSViresh Kumar 			 */
2296e77dcb0bSViresh Kumar 			genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
2297dd461cd9SStephan Gerhold 			WARN_ON(IS_ERR(genpd->opp_table));
22986a0ae73dSViresh Kumar 		}
22996a0ae73dSViresh Kumar 
23006a0ae73dSViresh Kumar 		genpd->provider = &np->fwnode;
23016a0ae73dSViresh Kumar 		genpd->has_provider = true;
23020159ec67SJon Hunter 	}
23030159ec67SJon Hunter 
230440845524SThierry Reding 	ret = genpd_add_provider(np, data->xlate, data);
2305de0aa06dSJon Hunter 	if (ret < 0)
2306de0aa06dSJon Hunter 		goto error;
2307de0aa06dSJon Hunter 
2308de0aa06dSJon Hunter 	mutex_unlock(&gpd_list_lock);
2309de0aa06dSJon Hunter 
2310de0aa06dSJon Hunter 	return 0;
2311de0aa06dSJon Hunter 
2312de0aa06dSJon Hunter error:
2313de0aa06dSJon Hunter 	while (i--) {
23146a0ae73dSViresh Kumar 		genpd = data->domains[i];
23156a0ae73dSViresh Kumar 
23166a0ae73dSViresh Kumar 		if (!genpd)
2317609bed67STomeu Vizoso 			continue;
23186a0ae73dSViresh Kumar 
23196a0ae73dSViresh Kumar 		genpd->provider = NULL;
23206a0ae73dSViresh Kumar 		genpd->has_provider = false;
23216a0ae73dSViresh Kumar 
23221067ae3eSViresh Kumar 		if (genpd->set_performance_state) {
23231067ae3eSViresh Kumar 			dev_pm_opp_put_opp_table(genpd->opp_table);
23246a0ae73dSViresh Kumar 			dev_pm_opp_of_remove_table(&genpd->dev);
2325de0aa06dSJon Hunter 		}
23261067ae3eSViresh Kumar 	}
23270159ec67SJon Hunter 
23280159ec67SJon Hunter 	mutex_unlock(&gpd_list_lock);
23290159ec67SJon Hunter 
23300159ec67SJon Hunter 	return ret;
2331892ebdccSJon Hunter }
2332892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell);
2333aa42240aSTomasz Figa 
2334aa42240aSTomasz Figa /**
2335aa42240aSTomasz Figa  * of_genpd_del_provider() - Remove a previously registered PM domain provider
2336aa42240aSTomasz Figa  * @np: Device node pointer associated with the PM domain provider
2337aa42240aSTomasz Figa  */
2338aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np)
2339aa42240aSTomasz Figa {
2340b556b15dSKrzysztof Kozlowski 	struct of_genpd_provider *cp, *tmp;
2341de0aa06dSJon Hunter 	struct generic_pm_domain *gpd;
2342aa42240aSTomasz Figa 
2343de0aa06dSJon Hunter 	mutex_lock(&gpd_list_lock);
2344aa42240aSTomasz Figa 	mutex_lock(&of_genpd_mutex);
2345b556b15dSKrzysztof Kozlowski 	list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) {
2346aa42240aSTomasz Figa 		if (cp->node == np) {
2347de0aa06dSJon Hunter 			/*
2348de0aa06dSJon Hunter 			 * For each PM domain associated with the
2349de0aa06dSJon Hunter 			 * provider, set the 'has_provider' to false
2350de0aa06dSJon Hunter 			 * so that the PM domain can be safely removed.
2351de0aa06dSJon Hunter 			 */
23526a0ae73dSViresh Kumar 			list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
23536a0ae73dSViresh Kumar 				if (gpd->provider == &np->fwnode) {
2354de0aa06dSJon Hunter 					gpd->has_provider = false;
2355de0aa06dSJon Hunter 
23566a0ae73dSViresh Kumar 					if (!gpd->set_performance_state)
23576a0ae73dSViresh Kumar 						continue;
23586a0ae73dSViresh Kumar 
23591067ae3eSViresh Kumar 					dev_pm_opp_put_opp_table(gpd->opp_table);
23606a0ae73dSViresh Kumar 					dev_pm_opp_of_remove_table(&gpd->dev);
23616a0ae73dSViresh Kumar 				}
23626a0ae73dSViresh Kumar 			}
23636a0ae73dSViresh Kumar 
2364bab2d712SSaravana Kannan 			fwnode_dev_initialized(&cp->node->fwnode, false);
2365aa42240aSTomasz Figa 			list_del(&cp->link);
2366aa42240aSTomasz Figa 			of_node_put(cp->node);
2367aa42240aSTomasz Figa 			kfree(cp);
2368aa42240aSTomasz Figa 			break;
2369aa42240aSTomasz Figa 		}
2370aa42240aSTomasz Figa 	}
2371aa42240aSTomasz Figa 	mutex_unlock(&of_genpd_mutex);
2372de0aa06dSJon Hunter 	mutex_unlock(&gpd_list_lock);
2373aa42240aSTomasz Figa }
2374aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider);
2375aa42240aSTomasz Figa 
2376aa42240aSTomasz Figa /**
2377f58d4e5aSJon Hunter  * genpd_get_from_provider() - Look-up PM domain
2378aa42240aSTomasz Figa  * @genpdspec: OF phandle args to use for look-up
2379aa42240aSTomasz Figa  *
2380aa42240aSTomasz Figa  * Looks for a PM domain provider under the node specified by @genpdspec and if
2381aa42240aSTomasz Figa  * found, uses xlate function of the provider to map phandle args to a PM
2382aa42240aSTomasz Figa  * domain.
2383aa42240aSTomasz Figa  *
2384aa42240aSTomasz Figa  * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
2385aa42240aSTomasz Figa  * on failure.
2386aa42240aSTomasz Figa  */
2387f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider(
2388aa42240aSTomasz Figa 					struct of_phandle_args *genpdspec)
2389aa42240aSTomasz Figa {
2390aa42240aSTomasz Figa 	struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
2391aa42240aSTomasz Figa 	struct of_genpd_provider *provider;
2392aa42240aSTomasz Figa 
239341795a8aSJon Hunter 	if (!genpdspec)
239441795a8aSJon Hunter 		return ERR_PTR(-EINVAL);
239541795a8aSJon Hunter 
2396aa42240aSTomasz Figa 	mutex_lock(&of_genpd_mutex);
2397aa42240aSTomasz Figa 
2398aa42240aSTomasz Figa 	/* Check if we have such a provider in our array */
2399aa42240aSTomasz Figa 	list_for_each_entry(provider, &of_genpd_providers, link) {
2400aa42240aSTomasz Figa 		if (provider->node == genpdspec->np)
2401aa42240aSTomasz Figa 			genpd = provider->xlate(genpdspec, provider->data);
2402aa42240aSTomasz Figa 		if (!IS_ERR(genpd))
2403aa42240aSTomasz Figa 			break;
2404aa42240aSTomasz Figa 	}
2405aa42240aSTomasz Figa 
2406aa42240aSTomasz Figa 	mutex_unlock(&of_genpd_mutex);
2407aa42240aSTomasz Figa 
2408aa42240aSTomasz Figa 	return genpd;
2409aa42240aSTomasz Figa }
2410aa42240aSTomasz Figa 
2411aa42240aSTomasz Figa /**
2412ec69572bSJon Hunter  * of_genpd_add_device() - Add a device to an I/O PM domain
2413ec69572bSJon Hunter  * @genpdspec: OF phandle args to use for look-up PM domain
2414ec69572bSJon Hunter  * @dev: Device to be added.
2415ec69572bSJon Hunter  *
2416ec69572bSJon Hunter  * Looks-up an I/O PM domain based upon phandle args provided and adds
2417ec69572bSJon Hunter  * the device to the PM domain. Returns a negative error code on failure.
2418ec69572bSJon Hunter  */
2419ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
2420ec69572bSJon Hunter {
2421ec69572bSJon Hunter 	struct generic_pm_domain *genpd;
242219efa5ffSJon Hunter 	int ret;
242319efa5ffSJon Hunter 
242419efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
2425ec69572bSJon Hunter 
2426f58d4e5aSJon Hunter 	genpd = genpd_get_from_provider(genpdspec);
242719efa5ffSJon Hunter 	if (IS_ERR(genpd)) {
242819efa5ffSJon Hunter 		ret = PTR_ERR(genpd);
242919efa5ffSJon Hunter 		goto out;
243019efa5ffSJon Hunter 	}
2431ec69572bSJon Hunter 
2432f9ccd7c3SUlf Hansson 	ret = genpd_add_device(genpd, dev, dev);
243319efa5ffSJon Hunter 
243419efa5ffSJon Hunter out:
243519efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
243619efa5ffSJon Hunter 
243719efa5ffSJon Hunter 	return ret;
2438ec69572bSJon Hunter }
2439ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device);
2440ec69572bSJon Hunter 
2441ec69572bSJon Hunter /**
2442ec69572bSJon Hunter  * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
2443ec69572bSJon Hunter  * @parent_spec: OF phandle args to use for parent PM domain look-up
2444ec69572bSJon Hunter  * @subdomain_spec: OF phandle args to use for subdomain look-up
2445ec69572bSJon Hunter  *
2446ec69572bSJon Hunter  * Looks-up a parent PM domain and subdomain based upon phandle args
2447ec69572bSJon Hunter  * provided and adds the subdomain to the parent PM domain. Returns a
2448ec69572bSJon Hunter  * negative error code on failure.
2449ec69572bSJon Hunter  */
2450ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
2451ec69572bSJon Hunter 			   struct of_phandle_args *subdomain_spec)
2452ec69572bSJon Hunter {
2453ec69572bSJon Hunter 	struct generic_pm_domain *parent, *subdomain;
245419efa5ffSJon Hunter 	int ret;
245519efa5ffSJon Hunter 
245619efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
2457ec69572bSJon Hunter 
2458f58d4e5aSJon Hunter 	parent = genpd_get_from_provider(parent_spec);
245919efa5ffSJon Hunter 	if (IS_ERR(parent)) {
246019efa5ffSJon Hunter 		ret = PTR_ERR(parent);
246119efa5ffSJon Hunter 		goto out;
246219efa5ffSJon Hunter 	}
2463ec69572bSJon Hunter 
2464f58d4e5aSJon Hunter 	subdomain = genpd_get_from_provider(subdomain_spec);
246519efa5ffSJon Hunter 	if (IS_ERR(subdomain)) {
246619efa5ffSJon Hunter 		ret = PTR_ERR(subdomain);
246719efa5ffSJon Hunter 		goto out;
246819efa5ffSJon Hunter 	}
2469ec69572bSJon Hunter 
247019efa5ffSJon Hunter 	ret = genpd_add_subdomain(parent, subdomain);
247119efa5ffSJon Hunter 
247219efa5ffSJon Hunter out:
247319efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
247419efa5ffSJon Hunter 
247518027d6fSDmitry Osipenko 	return ret == -ENOENT ? -EPROBE_DEFER : ret;
2476ec69572bSJon Hunter }
2477ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
2478ec69572bSJon Hunter 
2479ec69572bSJon Hunter /**
2480dedd1492SUlf Hansson  * of_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
2481dedd1492SUlf Hansson  * @parent_spec: OF phandle args to use for parent PM domain look-up
2482dedd1492SUlf Hansson  * @subdomain_spec: OF phandle args to use for subdomain look-up
2483dedd1492SUlf Hansson  *
2484dedd1492SUlf Hansson  * Looks-up a parent PM domain and subdomain based upon phandle args
2485dedd1492SUlf Hansson  * provided and removes the subdomain from the parent PM domain. Returns a
2486dedd1492SUlf Hansson  * negative error code on failure.
2487dedd1492SUlf Hansson  */
2488dedd1492SUlf Hansson int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
2489dedd1492SUlf Hansson 			      struct of_phandle_args *subdomain_spec)
2490dedd1492SUlf Hansson {
2491dedd1492SUlf Hansson 	struct generic_pm_domain *parent, *subdomain;
2492dedd1492SUlf Hansson 	int ret;
2493dedd1492SUlf Hansson 
2494dedd1492SUlf Hansson 	mutex_lock(&gpd_list_lock);
2495dedd1492SUlf Hansson 
2496dedd1492SUlf Hansson 	parent = genpd_get_from_provider(parent_spec);
2497dedd1492SUlf Hansson 	if (IS_ERR(parent)) {
2498dedd1492SUlf Hansson 		ret = PTR_ERR(parent);
2499dedd1492SUlf Hansson 		goto out;
2500dedd1492SUlf Hansson 	}
2501dedd1492SUlf Hansson 
2502dedd1492SUlf Hansson 	subdomain = genpd_get_from_provider(subdomain_spec);
2503dedd1492SUlf Hansson 	if (IS_ERR(subdomain)) {
2504dedd1492SUlf Hansson 		ret = PTR_ERR(subdomain);
2505dedd1492SUlf Hansson 		goto out;
2506dedd1492SUlf Hansson 	}
2507dedd1492SUlf Hansson 
2508dedd1492SUlf Hansson 	ret = pm_genpd_remove_subdomain(parent, subdomain);
2509dedd1492SUlf Hansson 
2510dedd1492SUlf Hansson out:
2511dedd1492SUlf Hansson 	mutex_unlock(&gpd_list_lock);
2512dedd1492SUlf Hansson 
2513dedd1492SUlf Hansson 	return ret;
2514dedd1492SUlf Hansson }
2515dedd1492SUlf Hansson EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain);
2516dedd1492SUlf Hansson 
2517dedd1492SUlf Hansson /**
251817926551SJon Hunter  * of_genpd_remove_last - Remove the last PM domain registered for a provider
2519763663c9SYang Yingliang  * @np: Pointer to device node associated with provider
252017926551SJon Hunter  *
252117926551SJon Hunter  * Find the last PM domain that was added by a particular provider and
252217926551SJon Hunter  * remove this PM domain from the list of PM domains. The provider is
252317926551SJon Hunter  * identified by the 'provider' device structure that is passed. The PM
252417926551SJon Hunter  * domain will only be removed, if the provider associated with domain
252517926551SJon Hunter  * has been removed.
252617926551SJon Hunter  *
252717926551SJon Hunter  * Returns a valid pointer to struct generic_pm_domain on success or
252817926551SJon Hunter  * ERR_PTR() on failure.
252917926551SJon Hunter  */
253017926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
253117926551SJon Hunter {
2532a7e2d1bcSKrzysztof Kozlowski 	struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT);
253317926551SJon Hunter 	int ret;
253417926551SJon Hunter 
253517926551SJon Hunter 	if (IS_ERR_OR_NULL(np))
253617926551SJon Hunter 		return ERR_PTR(-EINVAL);
253717926551SJon Hunter 
253817926551SJon Hunter 	mutex_lock(&gpd_list_lock);
2539a7e2d1bcSKrzysztof Kozlowski 	list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) {
254017926551SJon Hunter 		if (gpd->provider == &np->fwnode) {
254117926551SJon Hunter 			ret = genpd_remove(gpd);
254217926551SJon Hunter 			genpd = ret ? ERR_PTR(ret) : gpd;
254317926551SJon Hunter 			break;
254417926551SJon Hunter 		}
254517926551SJon Hunter 	}
254617926551SJon Hunter 	mutex_unlock(&gpd_list_lock);
254717926551SJon Hunter 
254817926551SJon Hunter 	return genpd;
254917926551SJon Hunter }
255017926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last);
255117926551SJon Hunter 
25523c095f32SUlf Hansson static void genpd_release_dev(struct device *dev)
25533c095f32SUlf Hansson {
2554e8b04de9SUlf Hansson 	of_node_put(dev->of_node);
25553c095f32SUlf Hansson 	kfree(dev);
25563c095f32SUlf Hansson }
25573c095f32SUlf Hansson 
25583c095f32SUlf Hansson static struct bus_type genpd_bus_type = {
25593c095f32SUlf Hansson 	.name		= "genpd",
25603c095f32SUlf Hansson };
25613c095f32SUlf Hansson 
256217926551SJon Hunter /**
2563aa42240aSTomasz Figa  * genpd_dev_pm_detach - Detach a device from its PM domain.
25648bb6944eSJon Hunter  * @dev: Device to detach.
2565aa42240aSTomasz Figa  * @power_off: Currently not used
2566aa42240aSTomasz Figa  *
2567aa42240aSTomasz Figa  * Try to locate a corresponding generic PM domain, which the device was
2568aa42240aSTomasz Figa  * attached to previously. If such is found, the device is detached from it.
2569aa42240aSTomasz Figa  */
2570aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off)
2571aa42240aSTomasz Figa {
2572446d999cSRussell King 	struct generic_pm_domain *pd;
257393af5e93SGeert Uytterhoeven 	unsigned int i;
2574aa42240aSTomasz Figa 	int ret = 0;
2575aa42240aSTomasz Figa 
257685168d56SUlf Hansson 	pd = dev_to_genpd(dev);
257785168d56SUlf Hansson 	if (IS_ERR(pd))
2578aa42240aSTomasz Figa 		return;
2579aa42240aSTomasz Figa 
2580aa42240aSTomasz Figa 	dev_dbg(dev, "removing from PM domain %s\n", pd->name);
2581aa42240aSTomasz Figa 
258293af5e93SGeert Uytterhoeven 	for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
258385168d56SUlf Hansson 		ret = genpd_remove_device(pd, dev);
2584aa42240aSTomasz Figa 		if (ret != -EAGAIN)
2585aa42240aSTomasz Figa 			break;
258693af5e93SGeert Uytterhoeven 
258793af5e93SGeert Uytterhoeven 		mdelay(i);
2588aa42240aSTomasz Figa 		cond_resched();
2589aa42240aSTomasz Figa 	}
2590aa42240aSTomasz Figa 
2591aa42240aSTomasz Figa 	if (ret < 0) {
2592aa42240aSTomasz Figa 		dev_err(dev, "failed to remove from PM domain %s: %d",
2593aa42240aSTomasz Figa 			pd->name, ret);
2594aa42240aSTomasz Figa 		return;
2595aa42240aSTomasz Figa 	}
2596aa42240aSTomasz Figa 
2597aa42240aSTomasz Figa 	/* Check if PM domain can be powered off after removing this device. */
2598aa42240aSTomasz Figa 	genpd_queue_power_off_work(pd);
25993c095f32SUlf Hansson 
26003c095f32SUlf Hansson 	/* Unregister the device if it was created by genpd. */
26013c095f32SUlf Hansson 	if (dev->bus == &genpd_bus_type)
26023c095f32SUlf Hansson 		device_unregister(dev);
2603aa42240aSTomasz Figa }
2604aa42240aSTomasz Figa 
2605632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev)
2606632f7ce3SRussell King {
2607632f7ce3SRussell King 	struct generic_pm_domain *pd;
2608632f7ce3SRussell King 
2609632f7ce3SRussell King 	pd = dev_to_genpd(dev);
2610632f7ce3SRussell King 	if (IS_ERR(pd))
2611632f7ce3SRussell King 		return;
2612632f7ce3SRussell King 
2613632f7ce3SRussell King 	genpd_queue_power_off_work(pd);
2614632f7ce3SRussell King }
2615632f7ce3SRussell King 
261651dcf748SUlf Hansson static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
261751dcf748SUlf Hansson 				 unsigned int index, bool power_on)
2618aa42240aSTomasz Figa {
2619aa42240aSTomasz Figa 	struct of_phandle_args pd_args;
2620aa42240aSTomasz Figa 	struct generic_pm_domain *pd;
2621aa42240aSTomasz Figa 	int ret;
2622aa42240aSTomasz Figa 
2623e8b04de9SUlf Hansson 	ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
26248cb1cbd6SUlf Hansson 				"#power-domain-cells", index, &pd_args);
2625001d50c9SGeert Uytterhoeven 	if (ret < 0)
2626bcd931f2SUlf Hansson 		return ret;
2627aa42240aSTomasz Figa 
262819efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
2629f58d4e5aSJon Hunter 	pd = genpd_get_from_provider(&pd_args);
2630265e2cf6SEric Anholt 	of_node_put(pd_args.np);
2631aa42240aSTomasz Figa 	if (IS_ERR(pd)) {
263219efa5ffSJon Hunter 		mutex_unlock(&gpd_list_lock);
2633aa42240aSTomasz Figa 		dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
2634aa42240aSTomasz Figa 			__func__, PTR_ERR(pd));
263551dcf748SUlf Hansson 		return driver_deferred_probe_check_state(base_dev);
2636aa42240aSTomasz Figa 	}
2637aa42240aSTomasz Figa 
2638aa42240aSTomasz Figa 	dev_dbg(dev, "adding to PM domain %s\n", pd->name);
2639aa42240aSTomasz Figa 
2640f9ccd7c3SUlf Hansson 	ret = genpd_add_device(pd, dev, base_dev);
264119efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
2642aa42240aSTomasz Figa 
2643aa42240aSTomasz Figa 	if (ret < 0) {
264434994692SGeert Uytterhoeven 		if (ret != -EPROBE_DEFER)
2645aa42240aSTomasz Figa 			dev_err(dev, "failed to add to PM domain %s: %d",
2646aa42240aSTomasz Figa 				pd->name, ret);
2647919b7308SUlf Hansson 		return ret;
2648aa42240aSTomasz Figa 	}
2649aa42240aSTomasz Figa 
2650aa42240aSTomasz Figa 	dev->pm_domain->detach = genpd_dev_pm_detach;
2651632f7ce3SRussell King 	dev->pm_domain->sync = genpd_dev_pm_sync;
2652aa42240aSTomasz Figa 
2653895b6612SUlf Hansson 	if (power_on) {
265435241d12SLina Iyer 		genpd_lock(pd);
265586e12eacSUlf Hansson 		ret = genpd_power_on(pd, 0);
265635241d12SLina Iyer 		genpd_unlock(pd);
2657895b6612SUlf Hansson 	}
265872038df3SUlf Hansson 
265972038df3SUlf Hansson 	if (ret)
266072038df3SUlf Hansson 		genpd_remove_device(pd, dev);
2661919b7308SUlf Hansson 
2662919b7308SUlf Hansson 	return ret ? -EPROBE_DEFER : 1;
2663aa42240aSTomasz Figa }
26648cb1cbd6SUlf Hansson 
26658cb1cbd6SUlf Hansson /**
26668cb1cbd6SUlf Hansson  * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
26678cb1cbd6SUlf Hansson  * @dev: Device to attach.
26688cb1cbd6SUlf Hansson  *
26698cb1cbd6SUlf Hansson  * Parse device's OF node to find a PM domain specifier. If such is found,
26708cb1cbd6SUlf Hansson  * attaches the device to retrieved pm_domain ops.
26718cb1cbd6SUlf Hansson  *
26728cb1cbd6SUlf Hansson  * Returns 1 on successfully attached PM domain, 0 when the device don't need a
26738cb1cbd6SUlf Hansson  * PM domain or when multiple power-domains exists for it, else a negative error
26748cb1cbd6SUlf Hansson  * code. Note that if a power-domain exists for the device, but it cannot be
26758cb1cbd6SUlf Hansson  * found or turned on, then return -EPROBE_DEFER to ensure that the device is
26768cb1cbd6SUlf Hansson  * not probed and to re-try again later.
26778cb1cbd6SUlf Hansson  */
26788cb1cbd6SUlf Hansson int genpd_dev_pm_attach(struct device *dev)
26798cb1cbd6SUlf Hansson {
26808cb1cbd6SUlf Hansson 	if (!dev->of_node)
26818cb1cbd6SUlf Hansson 		return 0;
26828cb1cbd6SUlf Hansson 
26838cb1cbd6SUlf Hansson 	/*
26848cb1cbd6SUlf Hansson 	 * Devices with multiple PM domains must be attached separately, as we
26858cb1cbd6SUlf Hansson 	 * can only attach one PM domain per device.
26868cb1cbd6SUlf Hansson 	 */
26878cb1cbd6SUlf Hansson 	if (of_count_phandle_with_args(dev->of_node, "power-domains",
26888cb1cbd6SUlf Hansson 				       "#power-domain-cells") != 1)
26898cb1cbd6SUlf Hansson 		return 0;
26908cb1cbd6SUlf Hansson 
269151dcf748SUlf Hansson 	return __genpd_dev_pm_attach(dev, dev, 0, true);
26928cb1cbd6SUlf Hansson }
2693aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
269430f60428SLina Iyer 
26953c095f32SUlf Hansson /**
26963c095f32SUlf Hansson  * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains.
26973c095f32SUlf Hansson  * @dev: The device used to lookup the PM domain.
26983c095f32SUlf Hansson  * @index: The index of the PM domain.
26993c095f32SUlf Hansson  *
27003c095f32SUlf Hansson  * Parse device's OF node to find a PM domain specifier at the provided @index.
27013c095f32SUlf Hansson  * If such is found, creates a virtual device and attaches it to the retrieved
27023c095f32SUlf Hansson  * pm_domain ops. To deal with detaching of the virtual device, the ->detach()
27033c095f32SUlf Hansson  * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach().
27043c095f32SUlf Hansson  *
27053c095f32SUlf Hansson  * Returns the created virtual device if successfully attached PM domain, NULL
27063c095f32SUlf Hansson  * when the device don't need a PM domain, else an ERR_PTR() in case of
27073c095f32SUlf Hansson  * failures. If a power-domain exists for the device, but cannot be found or
27083c095f32SUlf Hansson  * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device
27093c095f32SUlf Hansson  * is not probed and to re-try again later.
27103c095f32SUlf Hansson  */
27113c095f32SUlf Hansson struct device *genpd_dev_pm_attach_by_id(struct device *dev,
27123c095f32SUlf Hansson 					 unsigned int index)
27133c095f32SUlf Hansson {
2714560928b2SViresh Kumar 	struct device *virt_dev;
27153c095f32SUlf Hansson 	int num_domains;
27163c095f32SUlf Hansson 	int ret;
27173c095f32SUlf Hansson 
27183c095f32SUlf Hansson 	if (!dev->of_node)
27193c095f32SUlf Hansson 		return NULL;
27203c095f32SUlf Hansson 
27213ccf3f0cSUlf Hansson 	/* Verify that the index is within a valid range. */
27223c095f32SUlf Hansson 	num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
27233c095f32SUlf Hansson 						 "#power-domain-cells");
27243ccf3f0cSUlf Hansson 	if (index >= num_domains)
27253c095f32SUlf Hansson 		return NULL;
27263c095f32SUlf Hansson 
27273c095f32SUlf Hansson 	/* Allocate and register device on the genpd bus. */
2728560928b2SViresh Kumar 	virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
2729560928b2SViresh Kumar 	if (!virt_dev)
27303c095f32SUlf Hansson 		return ERR_PTR(-ENOMEM);
27313c095f32SUlf Hansson 
2732560928b2SViresh Kumar 	dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev));
2733560928b2SViresh Kumar 	virt_dev->bus = &genpd_bus_type;
2734560928b2SViresh Kumar 	virt_dev->release = genpd_release_dev;
2735e8b04de9SUlf Hansson 	virt_dev->of_node = of_node_get(dev->of_node);
27363c095f32SUlf Hansson 
2737560928b2SViresh Kumar 	ret = device_register(virt_dev);
27383c095f32SUlf Hansson 	if (ret) {
273971b77697SUlf Hansson 		put_device(virt_dev);
27403c095f32SUlf Hansson 		return ERR_PTR(ret);
27413c095f32SUlf Hansson 	}
27423c095f32SUlf Hansson 
27433c095f32SUlf Hansson 	/* Try to attach the device to the PM domain at the specified index. */
274451dcf748SUlf Hansson 	ret = __genpd_dev_pm_attach(virt_dev, dev, index, false);
27453c095f32SUlf Hansson 	if (ret < 1) {
2746560928b2SViresh Kumar 		device_unregister(virt_dev);
27473c095f32SUlf Hansson 		return ret ? ERR_PTR(ret) : NULL;
27483c095f32SUlf Hansson 	}
27493c095f32SUlf Hansson 
2750560928b2SViresh Kumar 	pm_runtime_enable(virt_dev);
2751560928b2SViresh Kumar 	genpd_queue_power_off_work(dev_to_genpd(virt_dev));
27523c095f32SUlf Hansson 
2753560928b2SViresh Kumar 	return virt_dev;
27543c095f32SUlf Hansson }
27553c095f32SUlf Hansson EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
27563c095f32SUlf Hansson 
27575d6be70aSUlf Hansson /**
27585d6be70aSUlf Hansson  * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains.
27595d6be70aSUlf Hansson  * @dev: The device used to lookup the PM domain.
27605d6be70aSUlf Hansson  * @name: The name of the PM domain.
27615d6be70aSUlf Hansson  *
27625d6be70aSUlf Hansson  * Parse device's OF node to find a PM domain specifier using the
27635d6be70aSUlf Hansson  * power-domain-names DT property. For further description see
27645d6be70aSUlf Hansson  * genpd_dev_pm_attach_by_id().
27655d6be70aSUlf Hansson  */
27667416f1f2SDouglas Anderson struct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name)
27675d6be70aSUlf Hansson {
27685d6be70aSUlf Hansson 	int index;
27695d6be70aSUlf Hansson 
27705d6be70aSUlf Hansson 	if (!dev->of_node)
27715d6be70aSUlf Hansson 		return NULL;
27725d6be70aSUlf Hansson 
27735d6be70aSUlf Hansson 	index = of_property_match_string(dev->of_node, "power-domain-names",
27745d6be70aSUlf Hansson 					 name);
27755d6be70aSUlf Hansson 	if (index < 0)
27765d6be70aSUlf Hansson 		return NULL;
27775d6be70aSUlf Hansson 
27785d6be70aSUlf Hansson 	return genpd_dev_pm_attach_by_id(dev, index);
27795d6be70aSUlf Hansson }
27805d6be70aSUlf Hansson 
278130f60428SLina Iyer static const struct of_device_id idle_state_match[] = {
2782598da548SLina Iyer 	{ .compatible = "domain-idle-state", },
278330f60428SLina Iyer 	{ }
278430f60428SLina Iyer };
278530f60428SLina Iyer 
278630f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state,
278730f60428SLina Iyer 				    struct device_node *state_node)
278830f60428SLina Iyer {
278930f60428SLina Iyer 	int err;
279030f60428SLina Iyer 	u32 residency;
279130f60428SLina Iyer 	u32 entry_latency, exit_latency;
279230f60428SLina Iyer 
279330f60428SLina Iyer 	err = of_property_read_u32(state_node, "entry-latency-us",
279430f60428SLina Iyer 						&entry_latency);
279530f60428SLina Iyer 	if (err) {
2796ea11e94bSRob Herring 		pr_debug(" * %pOF missing entry-latency-us property\n",
2797ea11e94bSRob Herring 			 state_node);
279830f60428SLina Iyer 		return -EINVAL;
279930f60428SLina Iyer 	}
280030f60428SLina Iyer 
280130f60428SLina Iyer 	err = of_property_read_u32(state_node, "exit-latency-us",
280230f60428SLina Iyer 						&exit_latency);
280330f60428SLina Iyer 	if (err) {
2804ea11e94bSRob Herring 		pr_debug(" * %pOF missing exit-latency-us property\n",
2805ea11e94bSRob Herring 			 state_node);
280630f60428SLina Iyer 		return -EINVAL;
280730f60428SLina Iyer 	}
280830f60428SLina Iyer 
280930f60428SLina Iyer 	err = of_property_read_u32(state_node, "min-residency-us", &residency);
281030f60428SLina Iyer 	if (!err)
281130f60428SLina Iyer 		genpd_state->residency_ns = 1000 * residency;
281230f60428SLina Iyer 
281330f60428SLina Iyer 	genpd_state->power_on_latency_ns = 1000 * exit_latency;
281430f60428SLina Iyer 	genpd_state->power_off_latency_ns = 1000 * entry_latency;
28150c9b694aSLina Iyer 	genpd_state->fwnode = &state_node->fwnode;
281630f60428SLina Iyer 
281730f60428SLina Iyer 	return 0;
281830f60428SLina Iyer }
281930f60428SLina Iyer 
2820a3381e3aSUlf Hansson static int genpd_iterate_idle_states(struct device_node *dn,
2821a3381e3aSUlf Hansson 				     struct genpd_power_state *states)
2822a3381e3aSUlf Hansson {
2823a3381e3aSUlf Hansson 	int ret;
2824a3381e3aSUlf Hansson 	struct of_phandle_iterator it;
2825a3381e3aSUlf Hansson 	struct device_node *np;
2826a3381e3aSUlf Hansson 	int i = 0;
2827a3381e3aSUlf Hansson 
2828a3381e3aSUlf Hansson 	ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL);
2829a3381e3aSUlf Hansson 	if (ret <= 0)
283056cb2689SUlf Hansson 		return ret == -ENOENT ? 0 : ret;
2831a3381e3aSUlf Hansson 
2832a3381e3aSUlf Hansson 	/* Loop over the phandles until all the requested entry is found */
2833a3381e3aSUlf Hansson 	of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) {
2834a3381e3aSUlf Hansson 		np = it.node;
2835a3381e3aSUlf Hansson 		if (!of_match_node(idle_state_match, np))
2836a3381e3aSUlf Hansson 			continue;
2837a3381e3aSUlf Hansson 		if (states) {
2838a3381e3aSUlf Hansson 			ret = genpd_parse_state(&states[i], np);
2839a3381e3aSUlf Hansson 			if (ret) {
2840a3381e3aSUlf Hansson 				pr_err("Parsing idle state node %pOF failed with err %d\n",
2841a3381e3aSUlf Hansson 				       np, ret);
2842a3381e3aSUlf Hansson 				of_node_put(np);
2843a3381e3aSUlf Hansson 				return ret;
2844a3381e3aSUlf Hansson 			}
2845a3381e3aSUlf Hansson 		}
2846a3381e3aSUlf Hansson 		i++;
2847a3381e3aSUlf Hansson 	}
2848a3381e3aSUlf Hansson 
2849a3381e3aSUlf Hansson 	return i;
2850a3381e3aSUlf Hansson }
2851a3381e3aSUlf Hansson 
285230f60428SLina Iyer /**
285330f60428SLina Iyer  * of_genpd_parse_idle_states: Return array of idle states for the genpd.
285430f60428SLina Iyer  *
285530f60428SLina Iyer  * @dn: The genpd device node
285630f60428SLina Iyer  * @states: The pointer to which the state array will be saved.
285730f60428SLina Iyer  * @n: The count of elements in the array returned from this function.
285830f60428SLina Iyer  *
285930f60428SLina Iyer  * Returns the device states parsed from the OF node. The memory for the states
286030f60428SLina Iyer  * is allocated by this function and is the responsibility of the caller to
28612c361684SUlf Hansson  * free the memory after use. If any or zero compatible domain idle states is
28622c361684SUlf Hansson  * found it returns 0 and in case of errors, a negative error code is returned.
286330f60428SLina Iyer  */
286430f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn,
286530f60428SLina Iyer 			struct genpd_power_state **states, int *n)
286630f60428SLina Iyer {
286730f60428SLina Iyer 	struct genpd_power_state *st;
2868a3381e3aSUlf Hansson 	int ret;
286930f60428SLina Iyer 
2870a3381e3aSUlf Hansson 	ret = genpd_iterate_idle_states(dn, NULL);
28712c361684SUlf Hansson 	if (ret < 0)
28722c361684SUlf Hansson 		return ret;
28732c361684SUlf Hansson 
28742c361684SUlf Hansson 	if (!ret) {
28752c361684SUlf Hansson 		*states = NULL;
28762c361684SUlf Hansson 		*n = 0;
28772c361684SUlf Hansson 		return 0;
28782c361684SUlf Hansson 	}
287930f60428SLina Iyer 
2880a3381e3aSUlf Hansson 	st = kcalloc(ret, sizeof(*st), GFP_KERNEL);
288130f60428SLina Iyer 	if (!st)
288230f60428SLina Iyer 		return -ENOMEM;
288330f60428SLina Iyer 
2884a3381e3aSUlf Hansson 	ret = genpd_iterate_idle_states(dn, st);
2885a3381e3aSUlf Hansson 	if (ret <= 0) {
288630f60428SLina Iyer 		kfree(st);
2887a3381e3aSUlf Hansson 		return ret < 0 ? ret : -EINVAL;
288830f60428SLina Iyer 	}
288930f60428SLina Iyer 
289030f60428SLina Iyer 	*states = st;
2891a3381e3aSUlf Hansson 	*n = ret;
289230f60428SLina Iyer 
289330f60428SLina Iyer 	return 0;
289430f60428SLina Iyer }
289530f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
289630f60428SLina Iyer 
28976e41766aSViresh Kumar /**
2898e38f89d3SViresh Kumar  * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node.
2899e38f89d3SViresh Kumar  *
2900e38f89d3SViresh Kumar  * @genpd_dev: Genpd's device for which the performance-state needs to be found.
2901e38f89d3SViresh Kumar  * @opp: struct dev_pm_opp of the OPP for which we need to find performance
2902e38f89d3SViresh Kumar  *	state.
2903e38f89d3SViresh Kumar  *
2904e38f89d3SViresh Kumar  * Returns performance state encoded in the OPP of the genpd. This calls
2905e38f89d3SViresh Kumar  * platform specific genpd->opp_to_performance_state() callback to translate
2906e38f89d3SViresh Kumar  * power domain OPP to performance state.
2907e38f89d3SViresh Kumar  *
2908e38f89d3SViresh Kumar  * Returns performance state on success and 0 on failure.
2909e38f89d3SViresh Kumar  */
2910e38f89d3SViresh Kumar unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev,
2911e38f89d3SViresh Kumar 					       struct dev_pm_opp *opp)
2912e38f89d3SViresh Kumar {
2913e38f89d3SViresh Kumar 	struct generic_pm_domain *genpd = NULL;
2914e38f89d3SViresh Kumar 	int state;
2915e38f89d3SViresh Kumar 
2916e38f89d3SViresh Kumar 	genpd = container_of(genpd_dev, struct generic_pm_domain, dev);
2917e38f89d3SViresh Kumar 
2918e38f89d3SViresh Kumar 	if (unlikely(!genpd->opp_to_performance_state))
2919e38f89d3SViresh Kumar 		return 0;
2920e38f89d3SViresh Kumar 
2921e38f89d3SViresh Kumar 	genpd_lock(genpd);
2922e38f89d3SViresh Kumar 	state = genpd->opp_to_performance_state(genpd, opp);
2923e38f89d3SViresh Kumar 	genpd_unlock(genpd);
2924e38f89d3SViresh Kumar 
2925e38f89d3SViresh Kumar 	return state;
2926e38f89d3SViresh Kumar }
2927e38f89d3SViresh Kumar EXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state);
2928e38f89d3SViresh Kumar 
29293c095f32SUlf Hansson static int __init genpd_bus_init(void)
29303c095f32SUlf Hansson {
29313c095f32SUlf Hansson 	return bus_register(&genpd_bus_type);
29323c095f32SUlf Hansson }
29333c095f32SUlf Hansson core_initcall(genpd_bus_init);
29343c095f32SUlf Hansson 
2935d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
29362bd5306aSMaciej Matraszek 
29372bd5306aSMaciej Matraszek 
29382bd5306aSMaciej Matraszek /***        debugfs support        ***/
29392bd5306aSMaciej Matraszek 
29408b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS
29412bd5306aSMaciej Matraszek /*
29422bd5306aSMaciej Matraszek  * TODO: This function is a slightly modified version of rtpm_status_show
2943d30d819dSRafael J. Wysocki  * from sysfs.c, so generalize it.
29442bd5306aSMaciej Matraszek  */
29452bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev)
29462bd5306aSMaciej Matraszek {
29472bd5306aSMaciej Matraszek 	static const char * const status_lookup[] = {
29482bd5306aSMaciej Matraszek 		[RPM_ACTIVE] = "active",
29492bd5306aSMaciej Matraszek 		[RPM_RESUMING] = "resuming",
29502bd5306aSMaciej Matraszek 		[RPM_SUSPENDED] = "suspended",
29512bd5306aSMaciej Matraszek 		[RPM_SUSPENDING] = "suspending"
29522bd5306aSMaciej Matraszek 	};
29532bd5306aSMaciej Matraszek 	const char *p = "";
29542bd5306aSMaciej Matraszek 
29552bd5306aSMaciej Matraszek 	if (dev->power.runtime_error)
29562bd5306aSMaciej Matraszek 		p = "error";
29572bd5306aSMaciej Matraszek 	else if (dev->power.disable_depth)
29582bd5306aSMaciej Matraszek 		p = "unsupported";
29592bd5306aSMaciej Matraszek 	else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup))
29602bd5306aSMaciej Matraszek 		p = status_lookup[dev->power.runtime_status];
29612bd5306aSMaciej Matraszek 	else
29622bd5306aSMaciej Matraszek 		WARN_ON(1);
29632bd5306aSMaciej Matraszek 
296445fbc464SDmitry Osipenko 	seq_printf(s, "%-25s  ", p);
296545fbc464SDmitry Osipenko }
296645fbc464SDmitry Osipenko 
296745fbc464SDmitry Osipenko static void perf_status_str(struct seq_file *s, struct device *dev)
296845fbc464SDmitry Osipenko {
296945fbc464SDmitry Osipenko 	struct generic_pm_domain_data *gpd_data;
297045fbc464SDmitry Osipenko 
297145fbc464SDmitry Osipenko 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
297245fbc464SDmitry Osipenko 	seq_put_decimal_ull(s, "", gpd_data->performance_state);
29732bd5306aSMaciej Matraszek }
29742bd5306aSMaciej Matraszek 
29759e9704eaSUlf Hansson static int genpd_summary_one(struct seq_file *s,
297666a5ca4bSKevin Hilman 			struct generic_pm_domain *genpd)
29772bd5306aSMaciej Matraszek {
29782bd5306aSMaciej Matraszek 	static const char * const status_lookup[] = {
297949f618e1SUlf Hansson 		[GENPD_STATE_ON] = "on",
298049f618e1SUlf Hansson 		[GENPD_STATE_OFF] = "off"
29812bd5306aSMaciej Matraszek 	};
29822bd5306aSMaciej Matraszek 	struct pm_domain_data *pm_data;
29832bd5306aSMaciej Matraszek 	const char *kobj_path;
29842bd5306aSMaciej Matraszek 	struct gpd_link *link;
29856954d432SGeert Uytterhoeven 	char state[16];
29862bd5306aSMaciej Matraszek 	int ret;
29872bd5306aSMaciej Matraszek 
298835241d12SLina Iyer 	ret = genpd_lock_interruptible(genpd);
29892bd5306aSMaciej Matraszek 	if (ret)
29902bd5306aSMaciej Matraszek 		return -ERESTARTSYS;
29912bd5306aSMaciej Matraszek 
299266a5ca4bSKevin Hilman 	if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup)))
29932bd5306aSMaciej Matraszek 		goto exit;
299441e2c8e0SUlf Hansson 	if (!genpd_status_on(genpd))
29950ba554e4SGeert Uytterhoeven 		snprintf(state, sizeof(state), "%s-%u",
29966954d432SGeert Uytterhoeven 			 status_lookup[genpd->status], genpd->state_idx);
2997fc5cbf0cSAxel Haslam 	else
29986954d432SGeert Uytterhoeven 		snprintf(state, sizeof(state), "%s",
29996954d432SGeert Uytterhoeven 			 status_lookup[genpd->status]);
300045fbc464SDmitry Osipenko 	seq_printf(s, "%-30s  %-50s %u", genpd->name, state, genpd->performance_state);
30012bd5306aSMaciej Matraszek 
30022bd5306aSMaciej Matraszek 	/*
30032bd5306aSMaciej Matraszek 	 * Modifications on the list require holding locks on both
30048d87ae48SKees Cook 	 * parent and child, so we are safe.
300566a5ca4bSKevin Hilman 	 * Also genpd->name is immutable.
30062bd5306aSMaciej Matraszek 	 */
30078d87ae48SKees Cook 	list_for_each_entry(link, &genpd->parent_links, parent_node) {
300845fbc464SDmitry Osipenko 		if (list_is_first(&link->parent_node, &genpd->parent_links))
300945fbc464SDmitry Osipenko 			seq_printf(s, "\n%48s", " ");
30108d87ae48SKees Cook 		seq_printf(s, "%s", link->child->name);
30118d87ae48SKees Cook 		if (!list_is_last(&link->parent_node, &genpd->parent_links))
30122bd5306aSMaciej Matraszek 			seq_puts(s, ", ");
30132bd5306aSMaciej Matraszek 	}
30142bd5306aSMaciej Matraszek 
301566a5ca4bSKevin Hilman 	list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
3016d716f479SLina Iyer 		kobj_path = kobject_get_path(&pm_data->dev->kobj,
3017d716f479SLina Iyer 				genpd_is_irq_safe(genpd) ?
3018d716f479SLina Iyer 				GFP_ATOMIC : GFP_KERNEL);
30192bd5306aSMaciej Matraszek 		if (kobj_path == NULL)
30202bd5306aSMaciej Matraszek 			continue;
30212bd5306aSMaciej Matraszek 
30222bd5306aSMaciej Matraszek 		seq_printf(s, "\n    %-50s  ", kobj_path);
30232bd5306aSMaciej Matraszek 		rtpm_status_str(s, pm_data->dev);
302445fbc464SDmitry Osipenko 		perf_status_str(s, pm_data->dev);
30252bd5306aSMaciej Matraszek 		kfree(kobj_path);
30262bd5306aSMaciej Matraszek 	}
30272bd5306aSMaciej Matraszek 
30282bd5306aSMaciej Matraszek 	seq_puts(s, "\n");
30292bd5306aSMaciej Matraszek exit:
303035241d12SLina Iyer 	genpd_unlock(genpd);
30312bd5306aSMaciej Matraszek 
30322bd5306aSMaciej Matraszek 	return 0;
30332bd5306aSMaciej Matraszek }
30342bd5306aSMaciej Matraszek 
3035d32dcc6cSYangtao Li static int summary_show(struct seq_file *s, void *data)
30362bd5306aSMaciej Matraszek {
303766a5ca4bSKevin Hilman 	struct generic_pm_domain *genpd;
30382bd5306aSMaciej Matraszek 	int ret = 0;
30392bd5306aSMaciej Matraszek 
304045fbc464SDmitry Osipenko 	seq_puts(s, "domain                          status          children                           performance\n");
30412bd5306aSMaciej Matraszek 	seq_puts(s, "    /device                                             runtime status\n");
304245fbc464SDmitry Osipenko 	seq_puts(s, "----------------------------------------------------------------------------------------------\n");
30432bd5306aSMaciej Matraszek 
30442bd5306aSMaciej Matraszek 	ret = mutex_lock_interruptible(&gpd_list_lock);
30452bd5306aSMaciej Matraszek 	if (ret)
30462bd5306aSMaciej Matraszek 		return -ERESTARTSYS;
30472bd5306aSMaciej Matraszek 
304866a5ca4bSKevin Hilman 	list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
30499e9704eaSUlf Hansson 		ret = genpd_summary_one(s, genpd);
30502bd5306aSMaciej Matraszek 		if (ret)
30512bd5306aSMaciej Matraszek 			break;
30522bd5306aSMaciej Matraszek 	}
30532bd5306aSMaciej Matraszek 	mutex_unlock(&gpd_list_lock);
30542bd5306aSMaciej Matraszek 
30552bd5306aSMaciej Matraszek 	return ret;
30562bd5306aSMaciej Matraszek }
30572bd5306aSMaciej Matraszek 
3058d32dcc6cSYangtao Li static int status_show(struct seq_file *s, void *data)
30592bd5306aSMaciej Matraszek {
3060b6a1d093SThara Gopinath 	static const char * const status_lookup[] = {
306149f618e1SUlf Hansson 		[GENPD_STATE_ON] = "on",
306249f618e1SUlf Hansson 		[GENPD_STATE_OFF] = "off"
3063b6a1d093SThara Gopinath 	};
3064b6a1d093SThara Gopinath 
3065b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3066b6a1d093SThara Gopinath 	int ret = 0;
3067b6a1d093SThara Gopinath 
3068b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3069b6a1d093SThara Gopinath 	if (ret)
3070b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3071b6a1d093SThara Gopinath 
3072b6a1d093SThara Gopinath 	if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup)))
3073b6a1d093SThara Gopinath 		goto exit;
3074b6a1d093SThara Gopinath 
307549f618e1SUlf Hansson 	if (genpd->status == GENPD_STATE_OFF)
3076b6a1d093SThara Gopinath 		seq_printf(s, "%s-%u\n", status_lookup[genpd->status],
3077b6a1d093SThara Gopinath 			genpd->state_idx);
3078b6a1d093SThara Gopinath 	else
3079b6a1d093SThara Gopinath 		seq_printf(s, "%s\n", status_lookup[genpd->status]);
3080b6a1d093SThara Gopinath exit:
3081b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3082b6a1d093SThara Gopinath 	return ret;
30832bd5306aSMaciej Matraszek }
30842bd5306aSMaciej Matraszek 
3085d32dcc6cSYangtao Li static int sub_domains_show(struct seq_file *s, void *data)
3086b6a1d093SThara Gopinath {
3087b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3088b6a1d093SThara Gopinath 	struct gpd_link *link;
3089b6a1d093SThara Gopinath 	int ret = 0;
3090b6a1d093SThara Gopinath 
3091b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3092b6a1d093SThara Gopinath 	if (ret)
3093b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3094b6a1d093SThara Gopinath 
30958d87ae48SKees Cook 	list_for_each_entry(link, &genpd->parent_links, parent_node)
30968d87ae48SKees Cook 		seq_printf(s, "%s\n", link->child->name);
3097b6a1d093SThara Gopinath 
3098b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3099b6a1d093SThara Gopinath 	return ret;
3100b6a1d093SThara Gopinath }
3101b6a1d093SThara Gopinath 
3102d32dcc6cSYangtao Li static int idle_states_show(struct seq_file *s, void *data)
3103b6a1d093SThara Gopinath {
3104b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3105b6a1d093SThara Gopinath 	unsigned int i;
3106b6a1d093SThara Gopinath 	int ret = 0;
3107b6a1d093SThara Gopinath 
3108b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3109b6a1d093SThara Gopinath 	if (ret)
3110b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3111b6a1d093SThara Gopinath 
3112c6a113b5SLina Iyer 	seq_puts(s, "State          Time Spent(ms) Usage          Rejected\n");
3113b6a1d093SThara Gopinath 
3114b6a1d093SThara Gopinath 	for (i = 0; i < genpd->state_count; i++) {
3115b6a1d093SThara Gopinath 		ktime_t delta = 0;
3116b6a1d093SThara Gopinath 		s64 msecs;
3117b6a1d093SThara Gopinath 
311849f618e1SUlf Hansson 		if ((genpd->status == GENPD_STATE_OFF) &&
3119b6a1d093SThara Gopinath 				(genpd->state_idx == i))
3120b6a1d093SThara Gopinath 			delta = ktime_sub(ktime_get(), genpd->accounting_time);
3121b6a1d093SThara Gopinath 
3122b6a1d093SThara Gopinath 		msecs = ktime_to_ms(
3123b6a1d093SThara Gopinath 			ktime_add(genpd->states[i].idle_time, delta));
3124c6a113b5SLina Iyer 		seq_printf(s, "S%-13i %-14lld %-14llu %llu\n", i, msecs,
3125c6a113b5SLina Iyer 			      genpd->states[i].usage, genpd->states[i].rejected);
3126b6a1d093SThara Gopinath 	}
3127b6a1d093SThara Gopinath 
3128b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3129b6a1d093SThara Gopinath 	return ret;
3130b6a1d093SThara Gopinath }
3131b6a1d093SThara Gopinath 
3132d32dcc6cSYangtao Li static int active_time_show(struct seq_file *s, void *data)
3133b6a1d093SThara Gopinath {
3134b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3135b6a1d093SThara Gopinath 	ktime_t delta = 0;
3136b6a1d093SThara Gopinath 	int ret = 0;
3137b6a1d093SThara Gopinath 
3138b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3139b6a1d093SThara Gopinath 	if (ret)
3140b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3141b6a1d093SThara Gopinath 
314249f618e1SUlf Hansson 	if (genpd->status == GENPD_STATE_ON)
3143b6a1d093SThara Gopinath 		delta = ktime_sub(ktime_get(), genpd->accounting_time);
3144b6a1d093SThara Gopinath 
3145b6a1d093SThara Gopinath 	seq_printf(s, "%lld ms\n", ktime_to_ms(
3146b6a1d093SThara Gopinath 				ktime_add(genpd->on_time, delta)));
3147b6a1d093SThara Gopinath 
3148b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3149b6a1d093SThara Gopinath 	return ret;
3150b6a1d093SThara Gopinath }
3151b6a1d093SThara Gopinath 
3152d32dcc6cSYangtao Li static int total_idle_time_show(struct seq_file *s, void *data)
3153b6a1d093SThara Gopinath {
3154b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3155b6a1d093SThara Gopinath 	ktime_t delta = 0, total = 0;
3156b6a1d093SThara Gopinath 	unsigned int i;
3157b6a1d093SThara Gopinath 	int ret = 0;
3158b6a1d093SThara Gopinath 
3159b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3160b6a1d093SThara Gopinath 	if (ret)
3161b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3162b6a1d093SThara Gopinath 
3163b6a1d093SThara Gopinath 	for (i = 0; i < genpd->state_count; i++) {
3164b6a1d093SThara Gopinath 
316549f618e1SUlf Hansson 		if ((genpd->status == GENPD_STATE_OFF) &&
3166b6a1d093SThara Gopinath 				(genpd->state_idx == i))
3167b6a1d093SThara Gopinath 			delta = ktime_sub(ktime_get(), genpd->accounting_time);
3168b6a1d093SThara Gopinath 
3169b6a1d093SThara Gopinath 		total = ktime_add(total, genpd->states[i].idle_time);
3170b6a1d093SThara Gopinath 	}
3171b6a1d093SThara Gopinath 	total = ktime_add(total, delta);
3172b6a1d093SThara Gopinath 
3173b6a1d093SThara Gopinath 	seq_printf(s, "%lld ms\n", ktime_to_ms(total));
3174b6a1d093SThara Gopinath 
3175b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3176b6a1d093SThara Gopinath 	return ret;
3177b6a1d093SThara Gopinath }
3178b6a1d093SThara Gopinath 
3179b6a1d093SThara Gopinath 
3180d32dcc6cSYangtao Li static int devices_show(struct seq_file *s, void *data)
3181b6a1d093SThara Gopinath {
3182b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3183b6a1d093SThara Gopinath 	struct pm_domain_data *pm_data;
3184b6a1d093SThara Gopinath 	const char *kobj_path;
3185b6a1d093SThara Gopinath 	int ret = 0;
3186b6a1d093SThara Gopinath 
3187b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3188b6a1d093SThara Gopinath 	if (ret)
3189b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3190b6a1d093SThara Gopinath 
3191b6a1d093SThara Gopinath 	list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
3192b6a1d093SThara Gopinath 		kobj_path = kobject_get_path(&pm_data->dev->kobj,
3193b6a1d093SThara Gopinath 				genpd_is_irq_safe(genpd) ?
3194b6a1d093SThara Gopinath 				GFP_ATOMIC : GFP_KERNEL);
3195b6a1d093SThara Gopinath 		if (kobj_path == NULL)
3196b6a1d093SThara Gopinath 			continue;
3197b6a1d093SThara Gopinath 
3198b6a1d093SThara Gopinath 		seq_printf(s, "%s\n", kobj_path);
3199b6a1d093SThara Gopinath 		kfree(kobj_path);
3200b6a1d093SThara Gopinath 	}
3201b6a1d093SThara Gopinath 
3202b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3203b6a1d093SThara Gopinath 	return ret;
3204b6a1d093SThara Gopinath }
3205b6a1d093SThara Gopinath 
3206d32dcc6cSYangtao Li static int perf_state_show(struct seq_file *s, void *data)
3207e8912812SRajendra Nayak {
3208e8912812SRajendra Nayak 	struct generic_pm_domain *genpd = s->private;
3209e8912812SRajendra Nayak 
3210e8912812SRajendra Nayak 	if (genpd_lock_interruptible(genpd))
3211e8912812SRajendra Nayak 		return -ERESTARTSYS;
3212e8912812SRajendra Nayak 
3213e8912812SRajendra Nayak 	seq_printf(s, "%u\n", genpd->performance_state);
3214e8912812SRajendra Nayak 
3215e8912812SRajendra Nayak 	genpd_unlock(genpd);
3216e8912812SRajendra Nayak 	return 0;
3217e8912812SRajendra Nayak }
3218e8912812SRajendra Nayak 
3219d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(summary);
3220d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(status);
3221d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(sub_domains);
3222d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(idle_states);
3223d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(active_time);
3224d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(total_idle_time);
3225d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(devices);
3226d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(perf_state);
32272bd5306aSMaciej Matraszek 
3228718072ceSThierry Strudel static void genpd_debug_add(struct generic_pm_domain *genpd)
32292bd5306aSMaciej Matraszek {
32302bd5306aSMaciej Matraszek 	struct dentry *d;
32312bd5306aSMaciej Matraszek 
3232718072ceSThierry Strudel 	if (!genpd_debugfs_dir)
3233718072ceSThierry Strudel 		return;
32342bd5306aSMaciej Matraszek 
32359e9704eaSUlf Hansson 	d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
3236b6a1d093SThara Gopinath 
3237b6a1d093SThara Gopinath 	debugfs_create_file("current_state", 0444,
3238d32dcc6cSYangtao Li 			    d, genpd, &status_fops);
3239b6a1d093SThara Gopinath 	debugfs_create_file("sub_domains", 0444,
3240d32dcc6cSYangtao Li 			    d, genpd, &sub_domains_fops);
3241b6a1d093SThara Gopinath 	debugfs_create_file("idle_states", 0444,
3242d32dcc6cSYangtao Li 			    d, genpd, &idle_states_fops);
3243b6a1d093SThara Gopinath 	debugfs_create_file("active_time", 0444,
3244d32dcc6cSYangtao Li 			    d, genpd, &active_time_fops);
3245b6a1d093SThara Gopinath 	debugfs_create_file("total_idle_time", 0444,
3246d32dcc6cSYangtao Li 			    d, genpd, &total_idle_time_fops);
3247b6a1d093SThara Gopinath 	debugfs_create_file("devices", 0444,
3248d32dcc6cSYangtao Li 			    d, genpd, &devices_fops);
3249e8912812SRajendra Nayak 	if (genpd->set_performance_state)
3250e8912812SRajendra Nayak 		debugfs_create_file("perf_state", 0444,
3251d32dcc6cSYangtao Li 				    d, genpd, &perf_state_fops);
3252b6a1d093SThara Gopinath }
3253b6a1d093SThara Gopinath 
3254718072ceSThierry Strudel static int __init genpd_debug_init(void)
3255718072ceSThierry Strudel {
3256718072ceSThierry Strudel 	struct generic_pm_domain *genpd;
3257718072ceSThierry Strudel 
3258718072ceSThierry Strudel 	genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
3259718072ceSThierry Strudel 
3260718072ceSThierry Strudel 	debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir,
3261718072ceSThierry Strudel 			    NULL, &summary_fops);
3262718072ceSThierry Strudel 
3263718072ceSThierry Strudel 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
3264718072ceSThierry Strudel 		genpd_debug_add(genpd);
3265718072ceSThierry Strudel 
32662bd5306aSMaciej Matraszek 	return 0;
32672bd5306aSMaciej Matraszek }
32689e9704eaSUlf Hansson late_initcall(genpd_debug_init);
32692bd5306aSMaciej Matraszek 
32709e9704eaSUlf Hansson static void __exit genpd_debug_exit(void)
32712bd5306aSMaciej Matraszek {
32729e9704eaSUlf Hansson 	debugfs_remove_recursive(genpd_debugfs_dir);
32732bd5306aSMaciej Matraszek }
32749e9704eaSUlf Hansson __exitcall(genpd_debug_exit);
32758b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */
3276