xref: /openbmc/linux/drivers/base/power/domain.c (revision 63e2bd10)
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 
genpd_lock_mtx(struct generic_pm_domain * genpd)5235241d12SLina Iyer static void genpd_lock_mtx(struct generic_pm_domain *genpd)
5335241d12SLina Iyer {
5435241d12SLina Iyer 	mutex_lock(&genpd->mlock);
5535241d12SLina Iyer }
5635241d12SLina Iyer 
genpd_lock_nested_mtx(struct generic_pm_domain * genpd,int depth)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 
genpd_lock_interruptible_mtx(struct generic_pm_domain * genpd)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 
genpd_unlock_mtx(struct generic_pm_domain * genpd)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 
genpd_lock_spin(struct generic_pm_domain * genpd)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 
genpd_lock_nested_spin(struct generic_pm_domain * genpd,int depth)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 
genpd_lock_interruptible_spin(struct generic_pm_domain * genpd)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 
genpd_unlock_spin(struct generic_pm_domain * genpd)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 
irq_safe_dev_in_sleep_domain(struct device * dev,const struct generic_pm_domain * genpd)1347a02444bSUlf Hansson static inline bool irq_safe_dev_in_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 	/*
1427a02444bSUlf Hansson 	 * Warn once if an IRQ safe device is attached to a domain, which
1437a02444bSUlf Hansson 	 * callbacks are allowed to sleep. This indicates a suboptimal
1447a02444bSUlf Hansson 	 * configuration for PM, but it doesn't matter for an always on domain.
145075c37d5SUlf Hansson 	 */
146bcc19f69SUlf Hansson 	if (genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd))
147bcc19f69SUlf Hansson 		return ret;
148bcc19f69SUlf Hansson 
149bcc19f69SUlf Hansson 	if (ret)
150d716f479SLina Iyer 		dev_warn_once(dev, "PM domain %s will not be powered off\n",
151d716f479SLina Iyer 				genpd->name);
152d716f479SLina Iyer 
153d716f479SLina Iyer 	return ret;
154d716f479SLina Iyer }
155d716f479SLina Iyer 
156b3ad17c0SUlf Hansson static int genpd_runtime_suspend(struct device *dev);
157b3ad17c0SUlf Hansson 
158446d999cSRussell King /*
159446d999cSRussell King  * Get the generic PM domain for a particular struct device.
160446d999cSRussell King  * This validates the struct device pointer, the PM domain pointer,
161446d999cSRussell King  * and checks that the PM domain pointer is a real generic PM domain.
162446d999cSRussell King  * Any failure results in NULL being returned.
163446d999cSRussell King  */
dev_to_genpd_safe(struct device * dev)164b3ad17c0SUlf Hansson static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev)
165446d999cSRussell King {
166446d999cSRussell King 	if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain))
167446d999cSRussell King 		return NULL;
168446d999cSRussell King 
169b3ad17c0SUlf Hansson 	/* A genpd's always have its ->runtime_suspend() callback assigned. */
170b3ad17c0SUlf Hansson 	if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend)
171b3ad17c0SUlf Hansson 		return pd_to_genpd(dev->pm_domain);
172446d999cSRussell King 
173b3ad17c0SUlf Hansson 	return NULL;
174446d999cSRussell King }
175446d999cSRussell King 
176446d999cSRussell King /*
177446d999cSRussell King  * This should only be used where we are certain that the pm_domain
178446d999cSRussell King  * attached to the device is a genpd domain.
179446d999cSRussell King  */
dev_to_genpd(struct device * dev)180446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev)
1815248051bSRafael J. Wysocki {
1825248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
1835248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
1845248051bSRafael J. Wysocki 
185596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
1865248051bSRafael J. Wysocki }
187f721889fSRafael J. Wysocki 
genpd_stop_dev(const struct generic_pm_domain * genpd,struct device * dev)188d8600c8bSKrzysztof Kozlowski static int genpd_stop_dev(const struct generic_pm_domain *genpd,
189d8600c8bSKrzysztof Kozlowski 			  struct device *dev)
190d5e4cbfeSRafael J. Wysocki {
19151cda844SUlf Hansson 	return GENPD_DEV_CALLBACK(genpd, int, stop, dev);
192d5e4cbfeSRafael J. Wysocki }
193d5e4cbfeSRafael J. Wysocki 
genpd_start_dev(const struct generic_pm_domain * genpd,struct device * dev)194d8600c8bSKrzysztof Kozlowski static int genpd_start_dev(const struct generic_pm_domain *genpd,
195d8600c8bSKrzysztof Kozlowski 			   struct device *dev)
196d5e4cbfeSRafael J. Wysocki {
197ba2bbfbfSUlf Hansson 	return GENPD_DEV_CALLBACK(genpd, int, start, dev);
198d5e4cbfeSRafael J. Wysocki }
199d5e4cbfeSRafael J. Wysocki 
genpd_sd_counter_dec(struct generic_pm_domain * genpd)200c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
201f721889fSRafael J. Wysocki {
202c4bb3160SRafael J. Wysocki 	bool ret = false;
203c4bb3160SRafael J. Wysocki 
204c4bb3160SRafael J. Wysocki 	if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
205c4bb3160SRafael J. Wysocki 		ret = !!atomic_dec_and_test(&genpd->sd_count);
206c4bb3160SRafael J. Wysocki 
207c4bb3160SRafael J. Wysocki 	return ret;
208c4bb3160SRafael J. Wysocki }
209c4bb3160SRafael J. Wysocki 
genpd_sd_counter_inc(struct generic_pm_domain * genpd)210c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
211c4bb3160SRafael J. Wysocki {
212c4bb3160SRafael J. Wysocki 	atomic_inc(&genpd->sd_count);
2134e857c58SPeter Zijlstra 	smp_mb__after_atomic();
214f721889fSRafael J. Wysocki }
215f721889fSRafael J. Wysocki 
216afece3abSThara Gopinath #ifdef CONFIG_DEBUG_FS
217718072ceSThierry Strudel static struct dentry *genpd_debugfs_dir;
218718072ceSThierry Strudel 
219718072ceSThierry Strudel static void genpd_debug_add(struct generic_pm_domain *genpd);
220718072ceSThierry Strudel 
genpd_debug_remove(struct generic_pm_domain * genpd)221718072ceSThierry Strudel static void genpd_debug_remove(struct generic_pm_domain *genpd)
222718072ceSThierry Strudel {
22337101d3cSHsin-Yi Wang 	if (!genpd_debugfs_dir)
22437101d3cSHsin-Yi Wang 		return;
22537101d3cSHsin-Yi Wang 
2260b6200e1SGreg Kroah-Hartman 	debugfs_lookup_and_remove(genpd->name, genpd_debugfs_dir);
227718072ceSThierry Strudel }
228718072ceSThierry Strudel 
genpd_update_accounting(struct generic_pm_domain * genpd)229afece3abSThara Gopinath static void genpd_update_accounting(struct generic_pm_domain *genpd)
230afece3abSThara Gopinath {
231bd40cbb0SUlf Hansson 	u64 delta, now;
232afece3abSThara Gopinath 
233bd40cbb0SUlf Hansson 	now = ktime_get_mono_fast_ns();
234bd40cbb0SUlf Hansson 	if (now <= genpd->accounting_time)
235bd40cbb0SUlf Hansson 		return;
236bd40cbb0SUlf Hansson 
237bd40cbb0SUlf Hansson 	delta = now - genpd->accounting_time;
238afece3abSThara Gopinath 
239afece3abSThara Gopinath 	/*
240afece3abSThara Gopinath 	 * If genpd->status is active, it means we are just
241afece3abSThara Gopinath 	 * out of off and so update the idle time and vice
242afece3abSThara Gopinath 	 * versa.
243afece3abSThara Gopinath 	 */
244bd40cbb0SUlf Hansson 	if (genpd->status == GENPD_STATE_ON)
245bd40cbb0SUlf Hansson 		genpd->states[genpd->state_idx].idle_time += delta;
246bd40cbb0SUlf Hansson 	else
247bd40cbb0SUlf Hansson 		genpd->on_time += delta;
248afece3abSThara Gopinath 
249afece3abSThara Gopinath 	genpd->accounting_time = now;
250afece3abSThara Gopinath }
251afece3abSThara Gopinath #else
genpd_debug_add(struct generic_pm_domain * genpd)252718072ceSThierry Strudel static inline void genpd_debug_add(struct generic_pm_domain *genpd) {}
genpd_debug_remove(struct generic_pm_domain * genpd)253718072ceSThierry Strudel static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}
genpd_update_accounting(struct generic_pm_domain * genpd)254afece3abSThara Gopinath static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
255afece3abSThara Gopinath #endif
256afece3abSThara Gopinath 
_genpd_reeval_performance_state(struct generic_pm_domain * genpd,unsigned int state)257cd50c6d3SViresh Kumar static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
258cd50c6d3SViresh Kumar 					   unsigned int state)
259cd50c6d3SViresh Kumar {
260cd50c6d3SViresh Kumar 	struct generic_pm_domain_data *pd_data;
261cd50c6d3SViresh Kumar 	struct pm_domain_data *pdd;
26218edf49cSViresh Kumar 	struct gpd_link *link;
263cd50c6d3SViresh Kumar 
264cd50c6d3SViresh Kumar 	/* New requested state is same as Max requested state */
265cd50c6d3SViresh Kumar 	if (state == genpd->performance_state)
266cd50c6d3SViresh Kumar 		return state;
267cd50c6d3SViresh Kumar 
268cd50c6d3SViresh Kumar 	/* New requested state is higher than Max requested state */
269cd50c6d3SViresh Kumar 	if (state > genpd->performance_state)
270cd50c6d3SViresh Kumar 		return state;
271cd50c6d3SViresh Kumar 
272cd50c6d3SViresh Kumar 	/* Traverse all devices within the domain */
273cd50c6d3SViresh Kumar 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
274cd50c6d3SViresh Kumar 		pd_data = to_gpd_data(pdd);
275cd50c6d3SViresh Kumar 
276cd50c6d3SViresh Kumar 		if (pd_data->performance_state > state)
277cd50c6d3SViresh Kumar 			state = pd_data->performance_state;
278cd50c6d3SViresh Kumar 	}
279cd50c6d3SViresh Kumar 
280cd50c6d3SViresh Kumar 	/*
28118edf49cSViresh Kumar 	 * Traverse all sub-domains within the domain. This can be
28218edf49cSViresh Kumar 	 * done without any additional locking as the link->performance_state
2838d87ae48SKees Cook 	 * field is protected by the parent genpd->lock, which is already taken.
28418edf49cSViresh Kumar 	 *
28518edf49cSViresh Kumar 	 * Also note that link->performance_state (subdomain's performance state
2868d87ae48SKees Cook 	 * requirement to parent domain) is different from
2878d87ae48SKees Cook 	 * link->child->performance_state (current performance state requirement
28818edf49cSViresh Kumar 	 * of the devices/sub-domains of the subdomain) and so can have a
28918edf49cSViresh Kumar 	 * different value.
29018edf49cSViresh Kumar 	 *
29118edf49cSViresh Kumar 	 * Note that we also take vote from powered-off sub-domains into account
29218edf49cSViresh Kumar 	 * as the same is done for devices right now.
293cd50c6d3SViresh Kumar 	 */
2948d87ae48SKees Cook 	list_for_each_entry(link, &genpd->parent_links, parent_node) {
29518edf49cSViresh Kumar 		if (link->performance_state > state)
29618edf49cSViresh Kumar 			state = link->performance_state;
29718edf49cSViresh Kumar 	}
29818edf49cSViresh Kumar 
299cd50c6d3SViresh Kumar 	return state;
300cd50c6d3SViresh Kumar }
301cd50c6d3SViresh Kumar 
genpd_xlate_performance_state(struct generic_pm_domain * genpd,struct generic_pm_domain * parent,unsigned int pstate)302079c42a0SDmitry Osipenko static int genpd_xlate_performance_state(struct generic_pm_domain *genpd,
303079c42a0SDmitry Osipenko 					 struct generic_pm_domain *parent,
304079c42a0SDmitry Osipenko 					 unsigned int pstate)
305079c42a0SDmitry Osipenko {
306079c42a0SDmitry Osipenko 	if (!parent->set_performance_state)
307079c42a0SDmitry Osipenko 		return pstate;
308079c42a0SDmitry Osipenko 
309079c42a0SDmitry Osipenko 	return dev_pm_opp_xlate_performance_state(genpd->opp_table,
310079c42a0SDmitry Osipenko 						  parent->opp_table,
311079c42a0SDmitry Osipenko 						  pstate);
312079c42a0SDmitry Osipenko }
313079c42a0SDmitry Osipenko 
_genpd_set_performance_state(struct generic_pm_domain * genpd,unsigned int state,int depth)314cd50c6d3SViresh Kumar static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
31518edf49cSViresh Kumar 					unsigned int state, int depth)
316cd50c6d3SViresh Kumar {
3178d87ae48SKees Cook 	struct generic_pm_domain *parent;
31818edf49cSViresh Kumar 	struct gpd_link *link;
3198d87ae48SKees Cook 	int parent_state, ret;
320cd50c6d3SViresh Kumar 
321cd50c6d3SViresh Kumar 	if (state == genpd->performance_state)
322cd50c6d3SViresh Kumar 		return 0;
323cd50c6d3SViresh Kumar 
3248d87ae48SKees Cook 	/* Propagate to parents of genpd */
3258d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
3268d87ae48SKees Cook 		parent = link->parent;
32718edf49cSViresh Kumar 
3288d87ae48SKees Cook 		/* Find parent's performance state */
329079c42a0SDmitry Osipenko 		ret = genpd_xlate_performance_state(genpd, parent, state);
33018edf49cSViresh Kumar 		if (unlikely(ret < 0))
33118edf49cSViresh Kumar 			goto err;
33218edf49cSViresh Kumar 
3338d87ae48SKees Cook 		parent_state = ret;
33418edf49cSViresh Kumar 
3358d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
33618edf49cSViresh Kumar 
33718edf49cSViresh Kumar 		link->prev_performance_state = link->performance_state;
3388d87ae48SKees Cook 		link->performance_state = parent_state;
3398d87ae48SKees Cook 		parent_state = _genpd_reeval_performance_state(parent,
3408d87ae48SKees Cook 						parent_state);
3418d87ae48SKees Cook 		ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
34218edf49cSViresh Kumar 		if (ret)
34318edf49cSViresh Kumar 			link->performance_state = link->prev_performance_state;
34418edf49cSViresh Kumar 
3458d87ae48SKees Cook 		genpd_unlock(parent);
34618edf49cSViresh Kumar 
34718edf49cSViresh Kumar 		if (ret)
34818edf49cSViresh Kumar 			goto err;
34918edf49cSViresh Kumar 	}
35018edf49cSViresh Kumar 
351079c42a0SDmitry Osipenko 	if (genpd->set_performance_state) {
352cd50c6d3SViresh Kumar 		ret = genpd->set_performance_state(genpd, state);
353cd50c6d3SViresh Kumar 		if (ret)
35418edf49cSViresh Kumar 			goto err;
355079c42a0SDmitry Osipenko 	}
356cd50c6d3SViresh Kumar 
357cd50c6d3SViresh Kumar 	genpd->performance_state = state;
358cd50c6d3SViresh Kumar 	return 0;
35918edf49cSViresh Kumar 
36018edf49cSViresh Kumar err:
36118edf49cSViresh Kumar 	/* Encountered an error, lets rollback */
3628d87ae48SKees Cook 	list_for_each_entry_continue_reverse(link, &genpd->child_links,
3638d87ae48SKees Cook 					     child_node) {
3648d87ae48SKees Cook 		parent = link->parent;
36518edf49cSViresh Kumar 
3668d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
36718edf49cSViresh Kumar 
3688d87ae48SKees Cook 		parent_state = link->prev_performance_state;
3698d87ae48SKees Cook 		link->performance_state = parent_state;
37018edf49cSViresh Kumar 
3718d87ae48SKees Cook 		parent_state = _genpd_reeval_performance_state(parent,
3728d87ae48SKees Cook 						parent_state);
3738d87ae48SKees Cook 		if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
37418edf49cSViresh Kumar 			pr_err("%s: Failed to roll back to %d performance state\n",
3758d87ae48SKees Cook 			       parent->name, parent_state);
37618edf49cSViresh Kumar 		}
37718edf49cSViresh Kumar 
3788d87ae48SKees Cook 		genpd_unlock(parent);
37918edf49cSViresh Kumar 	}
38018edf49cSViresh Kumar 
38118edf49cSViresh Kumar 	return ret;
382cd50c6d3SViresh Kumar }
383cd50c6d3SViresh Kumar 
genpd_set_performance_state(struct device * dev,unsigned int state)3840eef091dSUlf Hansson static int genpd_set_performance_state(struct device *dev, unsigned int state)
3850eef091dSUlf Hansson {
3860eef091dSUlf Hansson 	struct generic_pm_domain *genpd = dev_to_genpd(dev);
3870eef091dSUlf Hansson 	struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
3880eef091dSUlf Hansson 	unsigned int prev_state;
3890eef091dSUlf Hansson 	int ret;
3900eef091dSUlf Hansson 
3910eef091dSUlf Hansson 	prev_state = gpd_data->performance_state;
392d97fe100SUlf Hansson 	if (prev_state == state)
393d97fe100SUlf Hansson 		return 0;
394d97fe100SUlf Hansson 
3950eef091dSUlf Hansson 	gpd_data->performance_state = state;
3960eef091dSUlf Hansson 	state = _genpd_reeval_performance_state(genpd, state);
3970eef091dSUlf Hansson 
3980eef091dSUlf Hansson 	ret = _genpd_set_performance_state(genpd, state, 0);
3990eef091dSUlf Hansson 	if (ret)
4000eef091dSUlf Hansson 		gpd_data->performance_state = prev_state;
4010eef091dSUlf Hansson 
4020eef091dSUlf Hansson 	return ret;
4030eef091dSUlf Hansson }
4040eef091dSUlf Hansson 
genpd_drop_performance_state(struct device * dev)4055937c3ceSUlf Hansson static int genpd_drop_performance_state(struct device *dev)
4065937c3ceSUlf Hansson {
4075937c3ceSUlf Hansson 	unsigned int prev_state = dev_gpd_data(dev)->performance_state;
4085937c3ceSUlf Hansson 
4095937c3ceSUlf Hansson 	if (!genpd_set_performance_state(dev, 0))
4105937c3ceSUlf Hansson 		return prev_state;
4115937c3ceSUlf Hansson 
4125937c3ceSUlf Hansson 	return 0;
4135937c3ceSUlf Hansson }
4145937c3ceSUlf Hansson 
genpd_restore_performance_state(struct device * dev,unsigned int state)4155937c3ceSUlf Hansson static void genpd_restore_performance_state(struct device *dev,
4165937c3ceSUlf Hansson 					    unsigned int state)
4175937c3ceSUlf Hansson {
4185937c3ceSUlf Hansson 	if (state)
4195937c3ceSUlf Hansson 		genpd_set_performance_state(dev, state);
4205937c3ceSUlf Hansson }
4215937c3ceSUlf Hansson 
42242f6284aSViresh Kumar /**
42342f6284aSViresh Kumar  * dev_pm_genpd_set_performance_state- Set performance state of device's power
42442f6284aSViresh Kumar  * domain.
42542f6284aSViresh Kumar  *
42642f6284aSViresh Kumar  * @dev: Device for which the performance-state needs to be set.
42742f6284aSViresh Kumar  * @state: Target performance state of the device. This can be set as 0 when the
42842f6284aSViresh Kumar  *	   device doesn't have any performance state constraints left (And so
42942f6284aSViresh Kumar  *	   the device wouldn't participate anymore to find the target
43042f6284aSViresh Kumar  *	   performance state of the genpd).
43142f6284aSViresh Kumar  *
43242f6284aSViresh Kumar  * It is assumed that the users guarantee that the genpd wouldn't be detached
43342f6284aSViresh Kumar  * while this routine is getting called.
43442f6284aSViresh Kumar  *
43542f6284aSViresh Kumar  * Returns 0 on success and negative error values on failures.
43642f6284aSViresh Kumar  */
dev_pm_genpd_set_performance_state(struct device * dev,unsigned int state)43742f6284aSViresh Kumar int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
43842f6284aSViresh Kumar {
43942f6284aSViresh Kumar 	struct generic_pm_domain *genpd;
4403c5a2722SDmitry Osipenko 	int ret = 0;
44142f6284aSViresh Kumar 
4423ea4ca92SUlf Hansson 	genpd = dev_to_genpd_safe(dev);
4433ea4ca92SUlf Hansson 	if (!genpd)
44442f6284aSViresh Kumar 		return -ENODEV;
44542f6284aSViresh Kumar 
446e757e7faSYangtao Li 	if (WARN_ON(!dev->power.subsys_data ||
447e757e7faSYangtao Li 		     !dev->power.subsys_data->domain_data))
44842f6284aSViresh Kumar 		return -EINVAL;
44942f6284aSViresh Kumar 
45042f6284aSViresh Kumar 	genpd_lock(genpd);
4513c5a2722SDmitry Osipenko 	if (pm_runtime_suspended(dev)) {
4523c5a2722SDmitry Osipenko 		dev_gpd_data(dev)->rpm_pstate = state;
4533c5a2722SDmitry Osipenko 	} else {
4540eef091dSUlf Hansson 		ret = genpd_set_performance_state(dev, state);
4553c5a2722SDmitry Osipenko 		if (!ret)
4563c5a2722SDmitry Osipenko 			dev_gpd_data(dev)->rpm_pstate = 0;
4573c5a2722SDmitry Osipenko 	}
45842f6284aSViresh Kumar 	genpd_unlock(genpd);
45942f6284aSViresh Kumar 
46042f6284aSViresh Kumar 	return ret;
46142f6284aSViresh Kumar }
46242f6284aSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state);
46342f6284aSViresh Kumar 
46467e3242eSLina Iyer /**
46567e3242eSLina Iyer  * dev_pm_genpd_set_next_wakeup - Notify PM framework of an impending wakeup.
46667e3242eSLina Iyer  *
46767e3242eSLina Iyer  * @dev: Device to handle
46867e3242eSLina Iyer  * @next: impending interrupt/wakeup for the device
46967e3242eSLina Iyer  *
47067e3242eSLina Iyer  *
47167e3242eSLina Iyer  * Allow devices to inform of the next wakeup. It's assumed that the users
47267e3242eSLina Iyer  * guarantee that the genpd wouldn't be detached while this routine is getting
47367e3242eSLina Iyer  * called. Additionally, it's also assumed that @dev isn't runtime suspended
47467e3242eSLina Iyer  * (RPM_SUSPENDED)."
47567e3242eSLina Iyer  * Although devices are expected to update the next_wakeup after the end of
47667e3242eSLina Iyer  * their usecase as well, it is possible the devices themselves may not know
47767e3242eSLina Iyer  * about that, so stale @next will be ignored when powering off the domain.
47867e3242eSLina Iyer  */
dev_pm_genpd_set_next_wakeup(struct device * dev,ktime_t next)47967e3242eSLina Iyer void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next)
48067e3242eSLina Iyer {
48167e3242eSLina Iyer 	struct generic_pm_domain *genpd;
4829c74f2acSUlf Hansson 	struct gpd_timing_data *td;
48367e3242eSLina Iyer 
48467e3242eSLina Iyer 	genpd = dev_to_genpd_safe(dev);
48567e3242eSLina Iyer 	if (!genpd)
48667e3242eSLina Iyer 		return;
48767e3242eSLina Iyer 
4889c74f2acSUlf Hansson 	td = to_gpd_data(dev->power.subsys_data->domain_data)->td;
4899c74f2acSUlf Hansson 	if (td)
4909c74f2acSUlf Hansson 		td->next_wakeup = next;
49167e3242eSLina Iyer }
49267e3242eSLina Iyer EXPORT_SYMBOL_GPL(dev_pm_genpd_set_next_wakeup);
49367e3242eSLina Iyer 
4941498c503SMaulik Shah /**
4951498c503SMaulik Shah  * dev_pm_genpd_get_next_hrtimer - Return the next_hrtimer for the genpd
4961498c503SMaulik Shah  * @dev: A device that is attached to the genpd.
4971498c503SMaulik Shah  *
4981498c503SMaulik Shah  * This routine should typically be called for a device, at the point of when a
4991498c503SMaulik Shah  * GENPD_NOTIFY_PRE_OFF notification has been sent for it.
5001498c503SMaulik Shah  *
5011498c503SMaulik Shah  * Returns the aggregated value of the genpd's next hrtimer or KTIME_MAX if no
5021498c503SMaulik Shah  * valid value have been set.
5031498c503SMaulik Shah  */
dev_pm_genpd_get_next_hrtimer(struct device * dev)5041498c503SMaulik Shah ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev)
5051498c503SMaulik Shah {
5061498c503SMaulik Shah 	struct generic_pm_domain *genpd;
5071498c503SMaulik Shah 
5081498c503SMaulik Shah 	genpd = dev_to_genpd_safe(dev);
5091498c503SMaulik Shah 	if (!genpd)
5101498c503SMaulik Shah 		return KTIME_MAX;
5111498c503SMaulik Shah 
5121498c503SMaulik Shah 	if (genpd->gd)
5131498c503SMaulik Shah 		return genpd->gd->next_hrtimer;
5141498c503SMaulik Shah 
5151498c503SMaulik Shah 	return KTIME_MAX;
5161498c503SMaulik Shah }
5171498c503SMaulik Shah EXPORT_SYMBOL_GPL(dev_pm_genpd_get_next_hrtimer);
5181498c503SMaulik Shah 
519a9236a0aSUlf Hansson /*
520a9236a0aSUlf Hansson  * dev_pm_genpd_synced_poweroff - Next power off should be synchronous
521a9236a0aSUlf Hansson  *
522a9236a0aSUlf Hansson  * @dev: A device that is attached to the genpd.
523a9236a0aSUlf Hansson  *
524a9236a0aSUlf Hansson  * Allows a consumer of the genpd to notify the provider that the next power off
525a9236a0aSUlf Hansson  * should be synchronous.
526a9236a0aSUlf Hansson  *
527a9236a0aSUlf Hansson  * It is assumed that the users guarantee that the genpd wouldn't be detached
528a9236a0aSUlf Hansson  * while this routine is getting called.
529a9236a0aSUlf Hansson  */
dev_pm_genpd_synced_poweroff(struct device * dev)530a9236a0aSUlf Hansson void dev_pm_genpd_synced_poweroff(struct device *dev)
531a9236a0aSUlf Hansson {
532a9236a0aSUlf Hansson 	struct generic_pm_domain *genpd;
533a9236a0aSUlf Hansson 
534a9236a0aSUlf Hansson 	genpd = dev_to_genpd_safe(dev);
535a9236a0aSUlf Hansson 	if (!genpd)
536a9236a0aSUlf Hansson 		return;
537a9236a0aSUlf Hansson 
538a9236a0aSUlf Hansson 	genpd_lock(genpd);
539a9236a0aSUlf Hansson 	genpd->synced_poweroff = true;
540a9236a0aSUlf Hansson 	genpd_unlock(genpd);
541a9236a0aSUlf Hansson }
542a9236a0aSUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_synced_poweroff);
543a9236a0aSUlf Hansson 
_genpd_power_on(struct generic_pm_domain * genpd,bool timed)54486e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
545c8f0ea45SGeert Uytterhoeven {
546fc5cbf0cSAxel Haslam 	unsigned int state_idx = genpd->state_idx;
547c8f0ea45SGeert Uytterhoeven 	ktime_t time_start;
548c8f0ea45SGeert Uytterhoeven 	s64 elapsed_ns;
549330e3932SUlf Hansson 	int ret;
550d4f81383SUlf Hansson 
551d4f81383SUlf Hansson 	/* Notify consumers that we are about to power on. */
552330e3932SUlf Hansson 	ret = raw_notifier_call_chain_robust(&genpd->power_notifiers,
553330e3932SUlf Hansson 					     GENPD_NOTIFY_PRE_ON,
554330e3932SUlf Hansson 					     GENPD_NOTIFY_OFF, NULL);
555d4f81383SUlf Hansson 	ret = notifier_to_errno(ret);
556d4f81383SUlf Hansson 	if (ret)
557330e3932SUlf Hansson 		return ret;
558c8f0ea45SGeert Uytterhoeven 
559c8f0ea45SGeert Uytterhoeven 	if (!genpd->power_on)
560d4f81383SUlf Hansson 		goto out;
561c8f0ea45SGeert Uytterhoeven 
562b2a92f35SUlf Hansson 	timed = timed && genpd->gd && !genpd->states[state_idx].fwnode;
563d4f81383SUlf Hansson 	if (!timed) {
564d4f81383SUlf Hansson 		ret = genpd->power_on(genpd);
565d4f81383SUlf Hansson 		if (ret)
566d4f81383SUlf Hansson 			goto err;
567d4f81383SUlf Hansson 
568d4f81383SUlf Hansson 		goto out;
569d4f81383SUlf Hansson 	}
570a4630c61SGeert Uytterhoeven 
571c8f0ea45SGeert Uytterhoeven 	time_start = ktime_get();
572c8f0ea45SGeert Uytterhoeven 	ret = genpd->power_on(genpd);
573c8f0ea45SGeert Uytterhoeven 	if (ret)
574d4f81383SUlf Hansson 		goto err;
575c8f0ea45SGeert Uytterhoeven 
576c8f0ea45SGeert Uytterhoeven 	elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
577fc5cbf0cSAxel Haslam 	if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
578d4f81383SUlf Hansson 		goto out;
579c8f0ea45SGeert Uytterhoeven 
580fc5cbf0cSAxel Haslam 	genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
581f38d1a6dSUlf Hansson 	genpd->gd->max_off_time_changed = true;
5826d7d5c32SRussell King 	pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
583c8f0ea45SGeert Uytterhoeven 		 genpd->name, "on", elapsed_ns);
584c8f0ea45SGeert Uytterhoeven 
585d4f81383SUlf Hansson out:
586d4f81383SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
587a9236a0aSUlf Hansson 	genpd->synced_poweroff = false;
588d4f81383SUlf Hansson 	return 0;
589d4f81383SUlf Hansson err:
590d4f81383SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
591d4f81383SUlf Hansson 				NULL);
592c8f0ea45SGeert Uytterhoeven 	return ret;
593c8f0ea45SGeert Uytterhoeven }
594c8f0ea45SGeert Uytterhoeven 
_genpd_power_off(struct generic_pm_domain * genpd,bool timed)59586e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
596c8f0ea45SGeert Uytterhoeven {
597fc5cbf0cSAxel Haslam 	unsigned int state_idx = genpd->state_idx;
598c8f0ea45SGeert Uytterhoeven 	ktime_t time_start;
599c8f0ea45SGeert Uytterhoeven 	s64 elapsed_ns;
600330e3932SUlf Hansson 	int ret;
601d4f81383SUlf Hansson 
602d4f81383SUlf Hansson 	/* Notify consumers that we are about to power off. */
603330e3932SUlf Hansson 	ret = raw_notifier_call_chain_robust(&genpd->power_notifiers,
604330e3932SUlf Hansson 					     GENPD_NOTIFY_PRE_OFF,
605330e3932SUlf Hansson 					     GENPD_NOTIFY_ON, NULL);
606d4f81383SUlf Hansson 	ret = notifier_to_errno(ret);
607d4f81383SUlf Hansson 	if (ret)
608330e3932SUlf Hansson 		return ret;
609c8f0ea45SGeert Uytterhoeven 
610c8f0ea45SGeert Uytterhoeven 	if (!genpd->power_off)
611d4f81383SUlf Hansson 		goto out;
612c8f0ea45SGeert Uytterhoeven 
613b2a92f35SUlf Hansson 	timed = timed && genpd->gd && !genpd->states[state_idx].fwnode;
614d4f81383SUlf Hansson 	if (!timed) {
615d4f81383SUlf Hansson 		ret = genpd->power_off(genpd);
616d4f81383SUlf Hansson 		if (ret)
617d4f81383SUlf Hansson 			goto busy;
618d4f81383SUlf Hansson 
619d4f81383SUlf Hansson 		goto out;
620d4f81383SUlf Hansson 	}
621a4630c61SGeert Uytterhoeven 
622c8f0ea45SGeert Uytterhoeven 	time_start = ktime_get();
623c8f0ea45SGeert Uytterhoeven 	ret = genpd->power_off(genpd);
6240cec68a9SAisheng Dong 	if (ret)
625d4f81383SUlf Hansson 		goto busy;
626c8f0ea45SGeert Uytterhoeven 
627c8f0ea45SGeert Uytterhoeven 	elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
628fc5cbf0cSAxel Haslam 	if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
629d4f81383SUlf Hansson 		goto out;
630c8f0ea45SGeert Uytterhoeven 
631fc5cbf0cSAxel Haslam 	genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
632f38d1a6dSUlf Hansson 	genpd->gd->max_off_time_changed = true;
6336d7d5c32SRussell King 	pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
634c8f0ea45SGeert Uytterhoeven 		 genpd->name, "off", elapsed_ns);
635c8f0ea45SGeert Uytterhoeven 
636d4f81383SUlf Hansson out:
637d4f81383SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
638d4f81383SUlf Hansson 				NULL);
6390cec68a9SAisheng Dong 	return 0;
640d4f81383SUlf Hansson busy:
641330e3932SUlf Hansson 	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
642d4f81383SUlf Hansson 	return ret;
643c8f0ea45SGeert Uytterhoeven }
644c8f0ea45SGeert Uytterhoeven 
645f721889fSRafael J. Wysocki /**
64686e12eacSUlf Hansson  * genpd_queue_power_off_work - Queue up the execution of genpd_power_off().
647a3d09c73SMoritz Fischer  * @genpd: PM domain to power off.
64829e47e21SUlf Hansson  *
64986e12eacSUlf Hansson  * Queue up the execution of genpd_power_off() unless it's already been done
65029e47e21SUlf Hansson  * before.
65129e47e21SUlf Hansson  */
genpd_queue_power_off_work(struct generic_pm_domain * genpd)65229e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
65329e47e21SUlf Hansson {
65429e47e21SUlf Hansson 	queue_work(pm_wq, &genpd->power_off_work);
65529e47e21SUlf Hansson }
65629e47e21SUlf Hansson 
65729e47e21SUlf Hansson /**
6581f8728b7SUlf Hansson  * genpd_power_off - Remove power from a given PM domain.
6591f8728b7SUlf Hansson  * @genpd: PM domain to power down.
6603c64649dSUlf Hansson  * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the
6613c64649dSUlf Hansson  * RPM status of the releated device is in an intermediate state, not yet turned
6623c64649dSUlf Hansson  * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not
6633c64649dSUlf Hansson  * be RPM_SUSPENDED, while it tries to power off the PM domain.
664763663c9SYang Yingliang  * @depth: nesting count for lockdep.
6651f8728b7SUlf Hansson  *
6661f8728b7SUlf Hansson  * If all of the @genpd's devices have been suspended and all of its subdomains
6671f8728b7SUlf Hansson  * have been powered down, remove power from @genpd.
6681f8728b7SUlf Hansson  */
genpd_power_off(struct generic_pm_domain * genpd,bool one_dev_on,unsigned int depth)6692da83545SUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
6702da83545SUlf Hansson 			   unsigned int depth)
6711f8728b7SUlf Hansson {
6721f8728b7SUlf Hansson 	struct pm_domain_data *pdd;
6731f8728b7SUlf Hansson 	struct gpd_link *link;
6741f8728b7SUlf Hansson 	unsigned int not_suspended = 0;
675f63816e4SUlf Hansson 	int ret;
6761f8728b7SUlf Hansson 
6771f8728b7SUlf Hansson 	/*
6781f8728b7SUlf Hansson 	 * Do not try to power off the domain in the following situations:
6791f8728b7SUlf Hansson 	 * (1) The domain is already in the "power off" state.
6801f8728b7SUlf Hansson 	 * (2) System suspend is in progress.
6811f8728b7SUlf Hansson 	 */
68241e2c8e0SUlf Hansson 	if (!genpd_status_on(genpd) || genpd->prepared_count > 0)
6831f8728b7SUlf Hansson 		return 0;
6841f8728b7SUlf Hansson 
685ffaa42e8SUlf Hansson 	/*
686ffaa42e8SUlf Hansson 	 * Abort power off for the PM domain in the following situations:
687ffaa42e8SUlf Hansson 	 * (1) The domain is configured as always on.
688ffaa42e8SUlf Hansson 	 * (2) When the domain has a subdomain being powered on.
689ffaa42e8SUlf Hansson 	 */
690ed61e18aSLeonard Crestez 	if (genpd_is_always_on(genpd) ||
691ed61e18aSLeonard Crestez 			genpd_is_rpm_always_on(genpd) ||
692ed61e18aSLeonard Crestez 			atomic_read(&genpd->sd_count) > 0)
6931f8728b7SUlf Hansson 		return -EBUSY;
6941f8728b7SUlf Hansson 
695e7d90cfaSUlf Hansson 	/*
696e7d90cfaSUlf Hansson 	 * The children must be in their deepest (powered-off) states to allow
697e7d90cfaSUlf Hansson 	 * the parent to be powered off. Note that, there's no need for
698e7d90cfaSUlf Hansson 	 * additional locking, as powering on a child, requires the parent's
699e7d90cfaSUlf Hansson 	 * lock to be acquired first.
700e7d90cfaSUlf Hansson 	 */
701e7d90cfaSUlf Hansson 	list_for_each_entry(link, &genpd->parent_links, parent_node) {
702e7d90cfaSUlf Hansson 		struct generic_pm_domain *child = link->child;
703e7d90cfaSUlf Hansson 		if (child->state_idx < child->state_count - 1)
704e7d90cfaSUlf Hansson 			return -EBUSY;
705e7d90cfaSUlf Hansson 	}
706e7d90cfaSUlf Hansson 
7071f8728b7SUlf Hansson 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
7081f8728b7SUlf Hansson 		/*
7091f8728b7SUlf Hansson 		 * Do not allow PM domain to be powered off, when an IRQ safe
7101f8728b7SUlf Hansson 		 * device is part of a non-IRQ safe domain.
7111f8728b7SUlf Hansson 		 */
7121f8728b7SUlf Hansson 		if (!pm_runtime_suspended(pdd->dev) ||
7137a02444bSUlf Hansson 			irq_safe_dev_in_sleep_domain(pdd->dev, genpd))
7141f8728b7SUlf Hansson 			not_suspended++;
7151f8728b7SUlf Hansson 	}
7161f8728b7SUlf Hansson 
7173c64649dSUlf Hansson 	if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
7181f8728b7SUlf Hansson 		return -EBUSY;
7191f8728b7SUlf Hansson 
7201f8728b7SUlf Hansson 	if (genpd->gov && genpd->gov->power_down_ok) {
7211f8728b7SUlf Hansson 		if (!genpd->gov->power_down_ok(&genpd->domain))
7221f8728b7SUlf Hansson 			return -EAGAIN;
7231f8728b7SUlf Hansson 	}
7241f8728b7SUlf Hansson 
7252c9b7f87SUlf Hansson 	/* Default to shallowest state. */
7262c9b7f87SUlf Hansson 	if (!genpd->gov)
7272c9b7f87SUlf Hansson 		genpd->state_idx = 0;
7282c9b7f87SUlf Hansson 
729f63816e4SUlf Hansson 	/* Don't power off, if a child domain is waiting to power on. */
7301f8728b7SUlf Hansson 	if (atomic_read(&genpd->sd_count) > 0)
7311f8728b7SUlf Hansson 		return -EBUSY;
7321f8728b7SUlf Hansson 
7331f8728b7SUlf Hansson 	ret = _genpd_power_off(genpd, true);
734c6a113b5SLina Iyer 	if (ret) {
735c6a113b5SLina Iyer 		genpd->states[genpd->state_idx].rejected++;
7361f8728b7SUlf Hansson 		return ret;
737c6a113b5SLina Iyer 	}
7381f8728b7SUlf Hansson 
73949f618e1SUlf Hansson 	genpd->status = GENPD_STATE_OFF;
740afece3abSThara Gopinath 	genpd_update_accounting(genpd);
741c6a113b5SLina Iyer 	genpd->states[genpd->state_idx].usage++;
7421f8728b7SUlf Hansson 
7438d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
7448d87ae48SKees Cook 		genpd_sd_counter_dec(link->parent);
7458d87ae48SKees Cook 		genpd_lock_nested(link->parent, depth + 1);
7468d87ae48SKees Cook 		genpd_power_off(link->parent, false, depth + 1);
7478d87ae48SKees Cook 		genpd_unlock(link->parent);
7481f8728b7SUlf Hansson 	}
7491f8728b7SUlf Hansson 
7501f8728b7SUlf Hansson 	return 0;
7511f8728b7SUlf Hansson }
7521f8728b7SUlf Hansson 
7531f8728b7SUlf Hansson /**
7548d87ae48SKees Cook  * genpd_power_on - Restore power to a given PM domain and its parents.
7555248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
7560106ef51SMarek Szyprowski  * @depth: nesting count for lockdep.
7575248051bSRafael J. Wysocki  *
7588d87ae48SKees Cook  * Restore power to @genpd and all of its parents so that it is possible to
7595248051bSRafael J. Wysocki  * resume a device belonging to it.
7605248051bSRafael J. Wysocki  */
genpd_power_on(struct generic_pm_domain * genpd,unsigned int depth)76186e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
7625248051bSRafael J. Wysocki {
7635063ce15SRafael J. Wysocki 	struct gpd_link *link;
7645248051bSRafael J. Wysocki 	int ret = 0;
7655248051bSRafael J. Wysocki 
76641e2c8e0SUlf Hansson 	if (genpd_status_on(genpd))
7673f241775SRafael J. Wysocki 		return 0;
7685248051bSRafael J. Wysocki 
7695063ce15SRafael J. Wysocki 	/*
7705063ce15SRafael J. Wysocki 	 * The list is guaranteed not to change while the loop below is being
7718d87ae48SKees Cook 	 * executed, unless one of the parents' .power_on() callbacks fiddles
7725063ce15SRafael J. Wysocki 	 * with it.
7735063ce15SRafael J. Wysocki 	 */
7748d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
7758d87ae48SKees Cook 		struct generic_pm_domain *parent = link->parent;
7765248051bSRafael J. Wysocki 
7778d87ae48SKees Cook 		genpd_sd_counter_inc(parent);
7780106ef51SMarek Szyprowski 
7798d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
7808d87ae48SKees Cook 		ret = genpd_power_on(parent, depth + 1);
7818d87ae48SKees Cook 		genpd_unlock(parent);
7820106ef51SMarek Szyprowski 
7835063ce15SRafael J. Wysocki 		if (ret) {
7848d87ae48SKees Cook 			genpd_sd_counter_dec(parent);
7859e08cf42SRafael J. Wysocki 			goto err;
7865248051bSRafael J. Wysocki 		}
7875063ce15SRafael J. Wysocki 	}
7885248051bSRafael J. Wysocki 
78986e12eacSUlf Hansson 	ret = _genpd_power_on(genpd, true);
7909e08cf42SRafael J. Wysocki 	if (ret)
7919e08cf42SRafael J. Wysocki 		goto err;
7920140d8bdSRafael J. Wysocki 
79349f618e1SUlf Hansson 	genpd->status = GENPD_STATE_ON;
794afece3abSThara Gopinath 	genpd_update_accounting(genpd);
795afece3abSThara Gopinath 
7963f241775SRafael J. Wysocki 	return 0;
7979e08cf42SRafael J. Wysocki 
7989e08cf42SRafael J. Wysocki  err:
79929e47e21SUlf Hansson 	list_for_each_entry_continue_reverse(link,
8008d87ae48SKees Cook 					&genpd->child_links,
8018d87ae48SKees Cook 					child_node) {
8028d87ae48SKees Cook 		genpd_sd_counter_dec(link->parent);
8038d87ae48SKees Cook 		genpd_lock_nested(link->parent, depth + 1);
8048d87ae48SKees Cook 		genpd_power_off(link->parent, false, depth + 1);
8058d87ae48SKees Cook 		genpd_unlock(link->parent);
80629e47e21SUlf Hansson 	}
8079e08cf42SRafael J. Wysocki 
8083f241775SRafael J. Wysocki 	return ret;
8093f241775SRafael J. Wysocki }
8103f241775SRafael J. Wysocki 
genpd_dev_pm_start(struct device * dev)811ea71c596SUlf Hansson static int genpd_dev_pm_start(struct device *dev)
812ea71c596SUlf Hansson {
813ea71c596SUlf Hansson 	struct generic_pm_domain *genpd = dev_to_genpd(dev);
814ea71c596SUlf Hansson 
815ea71c596SUlf Hansson 	return genpd_start_dev(genpd, dev);
816ea71c596SUlf Hansson }
817ea71c596SUlf Hansson 
genpd_dev_pm_qos_notifier(struct notifier_block * nb,unsigned long val,void * ptr)8186ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
8196ff7bb0dSRafael J. Wysocki 				     unsigned long val, void *ptr)
8206ff7bb0dSRafael J. Wysocki {
8216ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
8226ff7bb0dSRafael J. Wysocki 	struct device *dev;
8236ff7bb0dSRafael J. Wysocki 
8246ff7bb0dSRafael J. Wysocki 	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
8256ff7bb0dSRafael J. Wysocki 	dev = gpd_data->base.dev;
8266ff7bb0dSRafael J. Wysocki 
8276ff7bb0dSRafael J. Wysocki 	for (;;) {
828f38d1a6dSUlf Hansson 		struct generic_pm_domain *genpd = ERR_PTR(-ENODATA);
8296ff7bb0dSRafael J. Wysocki 		struct pm_domain_data *pdd;
83066d29d80SUlf Hansson 		struct gpd_timing_data *td;
8316ff7bb0dSRafael J. Wysocki 
8326ff7bb0dSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
8336ff7bb0dSRafael J. Wysocki 
8346ff7bb0dSRafael J. Wysocki 		pdd = dev->power.subsys_data ?
8356ff7bb0dSRafael J. Wysocki 				dev->power.subsys_data->domain_data : NULL;
836b4883ca4SViresh Kumar 		if (pdd) {
83766d29d80SUlf Hansson 			td = to_gpd_data(pdd)->td;
838f38d1a6dSUlf Hansson 			if (td) {
83966d29d80SUlf Hansson 				td->constraint_changed = true;
8406ff7bb0dSRafael J. Wysocki 				genpd = dev_to_genpd(dev);
841f38d1a6dSUlf Hansson 			}
8426ff7bb0dSRafael J. Wysocki 		}
8436ff7bb0dSRafael J. Wysocki 
8446ff7bb0dSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
8456ff7bb0dSRafael J. Wysocki 
8466ff7bb0dSRafael J. Wysocki 		if (!IS_ERR(genpd)) {
84735241d12SLina Iyer 			genpd_lock(genpd);
848f38d1a6dSUlf Hansson 			genpd->gd->max_off_time_changed = true;
84935241d12SLina Iyer 			genpd_unlock(genpd);
8506ff7bb0dSRafael J. Wysocki 		}
8516ff7bb0dSRafael J. Wysocki 
8526ff7bb0dSRafael J. Wysocki 		dev = dev->parent;
8536ff7bb0dSRafael J. Wysocki 		if (!dev || dev->power.ignore_children)
8546ff7bb0dSRafael J. Wysocki 			break;
8556ff7bb0dSRafael J. Wysocki 	}
8566ff7bb0dSRafael J. Wysocki 
8576ff7bb0dSRafael J. Wysocki 	return NOTIFY_DONE;
8586ff7bb0dSRafael J. Wysocki }
8596ff7bb0dSRafael J. Wysocki 
8605248051bSRafael J. Wysocki /**
861f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
862f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
863f721889fSRafael J. Wysocki  */
genpd_power_off_work_fn(struct work_struct * work)864f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
865f721889fSRafael J. Wysocki {
866f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
867f721889fSRafael J. Wysocki 
868f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
869f721889fSRafael J. Wysocki 
87035241d12SLina Iyer 	genpd_lock(genpd);
8712da83545SUlf Hansson 	genpd_power_off(genpd, false, 0);
87235241d12SLina Iyer 	genpd_unlock(genpd);
873f721889fSRafael J. Wysocki }
874f721889fSRafael J. Wysocki 
875f721889fSRafael J. Wysocki /**
87654eeddbfSUlf Hansson  * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks
87754eeddbfSUlf Hansson  * @dev: Device to handle.
87854eeddbfSUlf Hansson  */
__genpd_runtime_suspend(struct device * dev)87954eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev)
88054eeddbfSUlf Hansson {
88154eeddbfSUlf Hansson 	int (*cb)(struct device *__dev);
88254eeddbfSUlf Hansson 
88354eeddbfSUlf Hansson 	if (dev->type && dev->type->pm)
88454eeddbfSUlf Hansson 		cb = dev->type->pm->runtime_suspend;
88554eeddbfSUlf Hansson 	else if (dev->class && dev->class->pm)
88654eeddbfSUlf Hansson 		cb = dev->class->pm->runtime_suspend;
88754eeddbfSUlf Hansson 	else if (dev->bus && dev->bus->pm)
88854eeddbfSUlf Hansson 		cb = dev->bus->pm->runtime_suspend;
88954eeddbfSUlf Hansson 	else
89054eeddbfSUlf Hansson 		cb = NULL;
89154eeddbfSUlf Hansson 
89254eeddbfSUlf Hansson 	if (!cb && dev->driver && dev->driver->pm)
89354eeddbfSUlf Hansson 		cb = dev->driver->pm->runtime_suspend;
89454eeddbfSUlf Hansson 
89554eeddbfSUlf Hansson 	return cb ? cb(dev) : 0;
89654eeddbfSUlf Hansson }
89754eeddbfSUlf Hansson 
89854eeddbfSUlf Hansson /**
89954eeddbfSUlf Hansson  * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks
90054eeddbfSUlf Hansson  * @dev: Device to handle.
90154eeddbfSUlf Hansson  */
__genpd_runtime_resume(struct device * dev)90254eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev)
90354eeddbfSUlf Hansson {
90454eeddbfSUlf Hansson 	int (*cb)(struct device *__dev);
90554eeddbfSUlf Hansson 
90654eeddbfSUlf Hansson 	if (dev->type && dev->type->pm)
90754eeddbfSUlf Hansson 		cb = dev->type->pm->runtime_resume;
90854eeddbfSUlf Hansson 	else if (dev->class && dev->class->pm)
90954eeddbfSUlf Hansson 		cb = dev->class->pm->runtime_resume;
91054eeddbfSUlf Hansson 	else if (dev->bus && dev->bus->pm)
91154eeddbfSUlf Hansson 		cb = dev->bus->pm->runtime_resume;
91254eeddbfSUlf Hansson 	else
91354eeddbfSUlf Hansson 		cb = NULL;
91454eeddbfSUlf Hansson 
91554eeddbfSUlf Hansson 	if (!cb && dev->driver && dev->driver->pm)
91654eeddbfSUlf Hansson 		cb = dev->driver->pm->runtime_resume;
91754eeddbfSUlf Hansson 
91854eeddbfSUlf Hansson 	return cb ? cb(dev) : 0;
91954eeddbfSUlf Hansson }
92054eeddbfSUlf Hansson 
92154eeddbfSUlf Hansson /**
922795bd2e7SUlf Hansson  * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
923f721889fSRafael J. Wysocki  * @dev: Device to suspend.
924f721889fSRafael J. Wysocki  *
925f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
926f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
927f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
928f721889fSRafael J. Wysocki  */
genpd_runtime_suspend(struct device * dev)929795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev)
930f721889fSRafael J. Wysocki {
931f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
9329df3921eSUlf Hansson 	bool (*suspend_ok)(struct device *__dev);
9335937c3ceSUlf Hansson 	struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
93466d29d80SUlf Hansson 	struct gpd_timing_data *td = gpd_data->td;
935ffe12855SUlf Hansson 	bool runtime_pm = pm_runtime_enabled(dev);
9363b84bf3cSUlf Hansson 	ktime_t time_start = 0;
9372b1d88cdSUlf Hansson 	s64 elapsed_ns;
938d5e4cbfeSRafael J. Wysocki 	int ret;
939f721889fSRafael J. Wysocki 
940f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
941f721889fSRafael J. Wysocki 
9425248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
9435248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
944f721889fSRafael J. Wysocki 		return -EINVAL;
945f721889fSRafael J. Wysocki 
946ffe12855SUlf Hansson 	/*
947ffe12855SUlf Hansson 	 * A runtime PM centric subsystem/driver may re-use the runtime PM
948ffe12855SUlf Hansson 	 * callbacks for other purposes than runtime PM. In those scenarios
949ffe12855SUlf Hansson 	 * runtime PM is disabled. Under these circumstances, we shall skip
950ffe12855SUlf Hansson 	 * validating/measuring the PM QoS latency.
951ffe12855SUlf Hansson 	 */
9529df3921eSUlf Hansson 	suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL;
9539df3921eSUlf Hansson 	if (runtime_pm && suspend_ok && !suspend_ok(dev))
954b02c999aSRafael J. Wysocki 		return -EBUSY;
955b02c999aSRafael J. Wysocki 
9562b1d88cdSUlf Hansson 	/* Measure suspend latency. */
9573b84bf3cSUlf Hansson 	if (td && runtime_pm)
9582b1d88cdSUlf Hansson 		time_start = ktime_get();
9592b1d88cdSUlf Hansson 
96054eeddbfSUlf Hansson 	ret = __genpd_runtime_suspend(dev);
961f721889fSRafael J. Wysocki 	if (ret)
96217b75ecaSRafael J. Wysocki 		return ret;
96317b75ecaSRafael J. Wysocki 
9642b1d88cdSUlf Hansson 	ret = genpd_stop_dev(genpd, dev);
965ba2bbfbfSUlf Hansson 	if (ret) {
96654eeddbfSUlf Hansson 		__genpd_runtime_resume(dev);
967ba2bbfbfSUlf Hansson 		return ret;
968ba2bbfbfSUlf Hansson 	}
969ba2bbfbfSUlf Hansson 
9702b1d88cdSUlf Hansson 	/* Update suspend latency value if the measured time exceeds it. */
9713b84bf3cSUlf Hansson 	if (td && runtime_pm) {
9722b1d88cdSUlf Hansson 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
9733b84bf3cSUlf Hansson 		if (elapsed_ns > td->suspend_latency_ns) {
9742b1d88cdSUlf Hansson 			td->suspend_latency_ns = elapsed_ns;
9752b1d88cdSUlf Hansson 			dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
9762b1d88cdSUlf Hansson 				elapsed_ns);
977f38d1a6dSUlf Hansson 			genpd->gd->max_off_time_changed = true;
9782b1d88cdSUlf Hansson 			td->constraint_changed = true;
9792b1d88cdSUlf Hansson 		}
980ffe12855SUlf Hansson 	}
9812b1d88cdSUlf Hansson 
9820aa2a221SRafael J. Wysocki 	/*
983d716f479SLina Iyer 	 * If power.irq_safe is set, this routine may be run with
984d716f479SLina Iyer 	 * IRQs disabled, so suspend only if the PM domain also is irq_safe.
9850aa2a221SRafael J. Wysocki 	 */
9867a02444bSUlf Hansson 	if (irq_safe_dev_in_sleep_domain(dev, genpd))
9870aa2a221SRafael J. Wysocki 		return 0;
9880aa2a221SRafael J. Wysocki 
98935241d12SLina Iyer 	genpd_lock(genpd);
9902da83545SUlf Hansson 	genpd_power_off(genpd, true, 0);
991ae8ac196SAbel Vesa 	gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
99235241d12SLina Iyer 	genpd_unlock(genpd);
993f721889fSRafael J. Wysocki 
994f721889fSRafael J. Wysocki 	return 0;
995f721889fSRafael J. Wysocki }
996f721889fSRafael J. Wysocki 
997f721889fSRafael J. Wysocki /**
998795bd2e7SUlf Hansson  * genpd_runtime_resume - Resume a device belonging to I/O PM domain.
999f721889fSRafael J. Wysocki  * @dev: Device to resume.
1000f721889fSRafael J. Wysocki  *
1001f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
1002f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1003f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
1004f721889fSRafael J. Wysocki  */
genpd_runtime_resume(struct device * dev)1005795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev)
1006f721889fSRafael J. Wysocki {
1007f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
10085937c3ceSUlf Hansson 	struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
100966d29d80SUlf Hansson 	struct gpd_timing_data *td = gpd_data->td;
10103b84bf3cSUlf Hansson 	bool timed = td && pm_runtime_enabled(dev);
10113b84bf3cSUlf Hansson 	ktime_t time_start = 0;
10122b1d88cdSUlf Hansson 	s64 elapsed_ns;
1013f721889fSRafael J. Wysocki 	int ret;
1014f721889fSRafael J. Wysocki 
1015f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1016f721889fSRafael J. Wysocki 
10175248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
10185248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
1019f721889fSRafael J. Wysocki 		return -EINVAL;
1020f721889fSRafael J. Wysocki 
1021d716f479SLina Iyer 	/*
1022d716f479SLina Iyer 	 * As we don't power off a non IRQ safe domain, which holds
1023d716f479SLina Iyer 	 * an IRQ safe device, we don't need to restore power to it.
1024d716f479SLina Iyer 	 */
1025a294237aSUlf Hansson 	if (irq_safe_dev_in_sleep_domain(dev, genpd))
1026ba2bbfbfSUlf Hansson 		goto out;
10270aa2a221SRafael J. Wysocki 
102835241d12SLina Iyer 	genpd_lock(genpd);
10295937c3ceSUlf Hansson 	genpd_restore_performance_state(dev, gpd_data->rpm_pstate);
1030ae8ac196SAbel Vesa 	ret = genpd_power_on(genpd, 0);
103135241d12SLina Iyer 	genpd_unlock(genpd);
1032ba2bbfbfSUlf Hansson 
1033ba2bbfbfSUlf Hansson 	if (ret)
10343f241775SRafael J. Wysocki 		return ret;
1035c6d22b37SRafael J. Wysocki 
1036ba2bbfbfSUlf Hansson  out:
10372b1d88cdSUlf Hansson 	/* Measure resume latency. */
10383b84bf3cSUlf Hansson 	if (timed)
10392b1d88cdSUlf Hansson 		time_start = ktime_get();
10402b1d88cdSUlf Hansson 
1041076395caSLaurent Pinchart 	ret = genpd_start_dev(genpd, dev);
1042076395caSLaurent Pinchart 	if (ret)
1043076395caSLaurent Pinchart 		goto err_poweroff;
1044076395caSLaurent Pinchart 
104554eeddbfSUlf Hansson 	ret = __genpd_runtime_resume(dev);
1046076395caSLaurent Pinchart 	if (ret)
1047076395caSLaurent Pinchart 		goto err_stop;
10482b1d88cdSUlf Hansson 
10492b1d88cdSUlf Hansson 	/* Update resume latency value if the measured time exceeds it. */
10503b84bf3cSUlf Hansson 	if (timed) {
10512b1d88cdSUlf Hansson 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
10523b84bf3cSUlf Hansson 		if (elapsed_ns > td->resume_latency_ns) {
10532b1d88cdSUlf Hansson 			td->resume_latency_ns = elapsed_ns;
10542b1d88cdSUlf Hansson 			dev_dbg(dev, "resume latency exceeded, %lld ns\n",
10552b1d88cdSUlf Hansson 				elapsed_ns);
1056f38d1a6dSUlf Hansson 			genpd->gd->max_off_time_changed = true;
10572b1d88cdSUlf Hansson 			td->constraint_changed = true;
10582b1d88cdSUlf Hansson 		}
10592b1d88cdSUlf Hansson 	}
106017b75ecaSRafael J. Wysocki 
1061f721889fSRafael J. Wysocki 	return 0;
1062076395caSLaurent Pinchart 
1063076395caSLaurent Pinchart err_stop:
1064076395caSLaurent Pinchart 	genpd_stop_dev(genpd, dev);
1065076395caSLaurent Pinchart err_poweroff:
10666dc466d3SAbaci Team 	if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) {
106735241d12SLina Iyer 		genpd_lock(genpd);
10682da83545SUlf Hansson 		genpd_power_off(genpd, true, 0);
1069ae8ac196SAbel Vesa 		gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
107035241d12SLina Iyer 		genpd_unlock(genpd);
1071076395caSLaurent Pinchart 	}
1072076395caSLaurent Pinchart 
1073076395caSLaurent Pinchart 	return ret;
1074f721889fSRafael J. Wysocki }
1075f721889fSRafael J. Wysocki 
107639ac5ba5STushar Behera static bool pd_ignore_unused;
pd_ignore_unused_setup(char * __unused)107739ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused)
107839ac5ba5STushar Behera {
107939ac5ba5STushar Behera 	pd_ignore_unused = true;
108039ac5ba5STushar Behera 	return 1;
108139ac5ba5STushar Behera }
108239ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup);
108339ac5ba5STushar Behera 
108417f2ae7fSRafael J. Wysocki /**
108586e12eacSUlf Hansson  * genpd_power_off_unused - Power off all PM domains with no devices in use.
108617f2ae7fSRafael J. Wysocki  */
genpd_power_off_unused(void)108786e12eacSUlf Hansson static int __init genpd_power_off_unused(void)
108817f2ae7fSRafael J. Wysocki {
108917f2ae7fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
109017f2ae7fSRafael J. Wysocki 
109139ac5ba5STushar Behera 	if (pd_ignore_unused) {
109239ac5ba5STushar Behera 		pr_warn("genpd: Not disabling unused power domains\n");
1093bb4b72fcSUlf Hansson 		return 0;
109439ac5ba5STushar Behera 	}
109539ac5ba5STushar Behera 
109617f2ae7fSRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
109717f2ae7fSRafael J. Wysocki 
109817f2ae7fSRafael J. Wysocki 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
109917f2ae7fSRafael J. Wysocki 		genpd_queue_power_off_work(genpd);
110017f2ae7fSRafael J. Wysocki 
110117f2ae7fSRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
110217f2ae7fSRafael J. Wysocki 
11032fe71dcdSUlf Hansson 	return 0;
11042fe71dcdSUlf Hansson }
1105*63e2bd10SKonrad Dybcio late_initcall_sync(genpd_power_off_unused);
11062fe71dcdSUlf Hansson 
11070159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP
11080159ec67SJon Hunter 
1109596ba34bSRafael J. Wysocki /**
11108d87ae48SKees Cook  * genpd_sync_power_off - Synchronously power off a PM domain and its parents.
1111596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
11120883ac03SUlf Hansson  * @use_lock: use the lock.
11130883ac03SUlf Hansson  * @depth: nesting count for lockdep.
1114596ba34bSRafael J. Wysocki  *
1115596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
11168d87ae48SKees Cook  * hibernation) and do that if so.  Also, in that case propagate to its parents.
1117596ba34bSRafael J. Wysocki  *
111877f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
11190883ac03SUlf Hansson  * transitions. The "noirq" callbacks may be executed asynchronously, thus in
11200883ac03SUlf Hansson  * these cases the lock must be held.
1121596ba34bSRafael J. Wysocki  */
genpd_sync_power_off(struct generic_pm_domain * genpd,bool use_lock,unsigned int depth)11220883ac03SUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
11230883ac03SUlf Hansson 				 unsigned int depth)
1124596ba34bSRafael J. Wysocki {
11255063ce15SRafael J. Wysocki 	struct gpd_link *link;
1126596ba34bSRafael J. Wysocki 
1127ffaa42e8SUlf Hansson 	if (!genpd_status_on(genpd) || genpd_is_always_on(genpd))
1128596ba34bSRafael J. Wysocki 		return;
1129596ba34bSRafael J. Wysocki 
1130c4bb3160SRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count
1131c4bb3160SRafael J. Wysocki 	    || atomic_read(&genpd->sd_count) > 0)
1132596ba34bSRafael J. Wysocki 		return;
1133596ba34bSRafael J. Wysocki 
1134e7d90cfaSUlf Hansson 	/* Check that the children are in their deepest (powered-off) state. */
1135e7d90cfaSUlf Hansson 	list_for_each_entry(link, &genpd->parent_links, parent_node) {
1136e7d90cfaSUlf Hansson 		struct generic_pm_domain *child = link->child;
1137e7d90cfaSUlf Hansson 		if (child->state_idx < child->state_count - 1)
1138e7d90cfaSUlf Hansson 			return;
1139e7d90cfaSUlf Hansson 	}
1140e7d90cfaSUlf Hansson 
1141fc5cbf0cSAxel Haslam 	/* Choose the deepest state when suspending */
1142fc5cbf0cSAxel Haslam 	genpd->state_idx = genpd->state_count - 1;
11431c14967cSUlf Hansson 	if (_genpd_power_off(genpd, false))
11441c14967cSUlf Hansson 		return;
1145596ba34bSRafael J. Wysocki 
114649f618e1SUlf Hansson 	genpd->status = GENPD_STATE_OFF;
11475063ce15SRafael J. Wysocki 
11488d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
11498d87ae48SKees Cook 		genpd_sd_counter_dec(link->parent);
11500883ac03SUlf Hansson 
11510883ac03SUlf Hansson 		if (use_lock)
11528d87ae48SKees Cook 			genpd_lock_nested(link->parent, depth + 1);
11530883ac03SUlf Hansson 
11548d87ae48SKees Cook 		genpd_sync_power_off(link->parent, use_lock, depth + 1);
11550883ac03SUlf Hansson 
11560883ac03SUlf Hansson 		if (use_lock)
11578d87ae48SKees Cook 			genpd_unlock(link->parent);
1158596ba34bSRafael J. Wysocki 	}
1159596ba34bSRafael J. Wysocki }
1160596ba34bSRafael J. Wysocki 
1161596ba34bSRafael J. Wysocki /**
11628d87ae48SKees Cook  * genpd_sync_power_on - Synchronously power on a PM domain and its parents.
1163802d8b49SRafael J. Wysocki  * @genpd: PM domain to power on.
11640883ac03SUlf Hansson  * @use_lock: use the lock.
11650883ac03SUlf Hansson  * @depth: nesting count for lockdep.
1166802d8b49SRafael J. Wysocki  *
116777f827deSRafael J. Wysocki  * This function is only called in "noirq" and "syscore" stages of system power
11680883ac03SUlf Hansson  * transitions. The "noirq" callbacks may be executed asynchronously, thus in
11690883ac03SUlf Hansson  * these cases the lock must be held.
1170802d8b49SRafael J. Wysocki  */
genpd_sync_power_on(struct generic_pm_domain * genpd,bool use_lock,unsigned int depth)11710883ac03SUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
11720883ac03SUlf Hansson 				unsigned int depth)
1173802d8b49SRafael J. Wysocki {
1174802d8b49SRafael J. Wysocki 	struct gpd_link *link;
1175802d8b49SRafael J. Wysocki 
117641e2c8e0SUlf Hansson 	if (genpd_status_on(genpd))
1177802d8b49SRafael J. Wysocki 		return;
1178802d8b49SRafael J. Wysocki 
11798d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
11808d87ae48SKees Cook 		genpd_sd_counter_inc(link->parent);
11810883ac03SUlf Hansson 
11820883ac03SUlf Hansson 		if (use_lock)
11838d87ae48SKees Cook 			genpd_lock_nested(link->parent, depth + 1);
11840883ac03SUlf Hansson 
11858d87ae48SKees Cook 		genpd_sync_power_on(link->parent, use_lock, depth + 1);
11860883ac03SUlf Hansson 
11870883ac03SUlf Hansson 		if (use_lock)
11888d87ae48SKees Cook 			genpd_unlock(link->parent);
1189802d8b49SRafael J. Wysocki 	}
1190802d8b49SRafael J. Wysocki 
119186e12eacSUlf Hansson 	_genpd_power_on(genpd, false);
119249f618e1SUlf Hansson 	genpd->status = GENPD_STATE_ON;
1193802d8b49SRafael J. Wysocki }
1194802d8b49SRafael J. Wysocki 
1195802d8b49SRafael J. Wysocki /**
11969e9704eaSUlf Hansson  * genpd_prepare - Start power transition of a device in a PM domain.
1197596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
1198596ba34bSRafael J. Wysocki  *
1199596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
1200596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
1201596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
1202596ba34bSRafael J. Wysocki  * consisting of I/O devices.
1203596ba34bSRafael J. Wysocki  */
genpd_prepare(struct device * dev)12049e9704eaSUlf Hansson static int genpd_prepare(struct device *dev)
1205596ba34bSRafael J. Wysocki {
1206596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1207b6c10c84SRafael J. Wysocki 	int ret;
1208596ba34bSRafael J. Wysocki 
1209596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1210596ba34bSRafael J. Wysocki 
1211596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1212596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1213596ba34bSRafael J. Wysocki 		return -EINVAL;
1214596ba34bSRafael J. Wysocki 
121535241d12SLina Iyer 	genpd_lock(genpd);
1216596ba34bSRafael J. Wysocki 
121739dd0f23SUlf Hansson 	if (genpd->prepared_count++ == 0)
121865533bbfSRafael J. Wysocki 		genpd->suspended_count = 0;
121917b75ecaSRafael J. Wysocki 
122035241d12SLina Iyer 	genpd_unlock(genpd);
1221596ba34bSRafael J. Wysocki 
1222b6c10c84SRafael J. Wysocki 	ret = pm_generic_prepare(dev);
12235241ab40SUlf Hansson 	if (ret < 0) {
122435241d12SLina Iyer 		genpd_lock(genpd);
1225b6c10c84SRafael J. Wysocki 
122639dd0f23SUlf Hansson 		genpd->prepared_count--;
1227b6c10c84SRafael J. Wysocki 
122835241d12SLina Iyer 		genpd_unlock(genpd);
1229b6c10c84SRafael J. Wysocki 	}
123017b75ecaSRafael J. Wysocki 
12315241ab40SUlf Hansson 	/* Never return 1, as genpd don't cope with the direct_complete path. */
12325241ab40SUlf Hansson 	return ret >= 0 ? 0 : ret;
1233596ba34bSRafael J. Wysocki }
1234596ba34bSRafael J. Wysocki 
1235596ba34bSRafael J. Wysocki /**
123610da6542SMikko Perttunen  * genpd_finish_suspend - Completion of suspend or hibernation of device in an
123710da6542SMikko Perttunen  *   I/O pm domain.
12380496c8aeSRafael J. Wysocki  * @dev: Device to suspend.
1239615db6d9SShawn Guo  * @suspend_noirq: Generic suspend_noirq callback.
1240615db6d9SShawn Guo  * @resume_noirq: Generic resume_noirq callback.
12410496c8aeSRafael J. Wysocki  *
12420496c8aeSRafael J. Wysocki  * Stop the device and remove power from the domain if all devices in it have
12430496c8aeSRafael J. Wysocki  * been stopped.
12440496c8aeSRafael J. Wysocki  */
genpd_finish_suspend(struct device * dev,int (* suspend_noirq)(struct device * dev),int (* resume_noirq)(struct device * dev))1245615db6d9SShawn Guo static int genpd_finish_suspend(struct device *dev,
1246615db6d9SShawn Guo 				int (*suspend_noirq)(struct device *dev),
1247615db6d9SShawn Guo 				int (*resume_noirq)(struct device *dev))
12480496c8aeSRafael J. Wysocki {
12490496c8aeSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1250a935424bSUlf Hansson 	int ret = 0;
1251596ba34bSRafael J. Wysocki 
12520496c8aeSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
12530496c8aeSRafael J. Wysocki 	if (IS_ERR(genpd))
12540496c8aeSRafael J. Wysocki 		return -EINVAL;
12550496c8aeSRafael J. Wysocki 
1256615db6d9SShawn Guo 	ret = suspend_noirq(dev);
125710da6542SMikko Perttunen 	if (ret)
125810da6542SMikko Perttunen 		return ret;
125910da6542SMikko Perttunen 
12604e1d9a73SPatrice Chotard 	if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
1261a935424bSUlf Hansson 		return 0;
1262a935424bSUlf Hansson 
126317218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
126417218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev)) {
126517218e00SRafael J. Wysocki 		ret = genpd_stop_dev(genpd, dev);
1266a935424bSUlf Hansson 		if (ret) {
1267615db6d9SShawn Guo 			resume_noirq(dev);
1268122a2237SUlf Hansson 			return ret;
1269122a2237SUlf Hansson 		}
1270a935424bSUlf Hansson 	}
1271122a2237SUlf Hansson 
12720883ac03SUlf Hansson 	genpd_lock(genpd);
1273596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
12740883ac03SUlf Hansson 	genpd_sync_power_off(genpd, true, 0);
12750883ac03SUlf Hansson 	genpd_unlock(genpd);
1276596ba34bSRafael J. Wysocki 
1277596ba34bSRafael J. Wysocki 	return 0;
1278596ba34bSRafael J. Wysocki }
1279596ba34bSRafael J. Wysocki 
1280596ba34bSRafael J. Wysocki /**
12819e9704eaSUlf Hansson  * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
128210da6542SMikko Perttunen  * @dev: Device to suspend.
128310da6542SMikko Perttunen  *
128410da6542SMikko Perttunen  * Stop the device and remove power from the domain if all devices in it have
128510da6542SMikko Perttunen  * been stopped.
128610da6542SMikko Perttunen  */
genpd_suspend_noirq(struct device * dev)12879e9704eaSUlf Hansson static int genpd_suspend_noirq(struct device *dev)
128810da6542SMikko Perttunen {
128910da6542SMikko Perttunen 	dev_dbg(dev, "%s()\n", __func__);
129010da6542SMikko Perttunen 
1291615db6d9SShawn Guo 	return genpd_finish_suspend(dev,
1292615db6d9SShawn Guo 				    pm_generic_suspend_noirq,
1293615db6d9SShawn Guo 				    pm_generic_resume_noirq);
129410da6542SMikko Perttunen }
129510da6542SMikko Perttunen 
129610da6542SMikko Perttunen /**
1297d9cc34feSShawn Guo  * genpd_finish_resume - Completion of resume of device in an I/O PM domain.
1298596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1299d9cc34feSShawn Guo  * @resume_noirq: Generic resume_noirq callback.
1300596ba34bSRafael J. Wysocki  *
13010496c8aeSRafael J. Wysocki  * Restore power to the device's PM domain, if necessary, and start the device.
1302596ba34bSRafael J. Wysocki  */
genpd_finish_resume(struct device * dev,int (* resume_noirq)(struct device * dev))1303d9cc34feSShawn Guo static int genpd_finish_resume(struct device *dev,
1304d9cc34feSShawn Guo 			       int (*resume_noirq)(struct device *dev))
1305596ba34bSRafael J. Wysocki {
1306596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1307a935424bSUlf Hansson 	int ret;
1308596ba34bSRafael J. Wysocki 
1309596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1310596ba34bSRafael J. Wysocki 
1311596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1312596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1313596ba34bSRafael J. Wysocki 		return -EINVAL;
1314596ba34bSRafael J. Wysocki 
13154e1d9a73SPatrice Chotard 	if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
1316d9cc34feSShawn Guo 		return resume_noirq(dev);
1317596ba34bSRafael J. Wysocki 
13180883ac03SUlf Hansson 	genpd_lock(genpd);
13190883ac03SUlf Hansson 	genpd_sync_power_on(genpd, true, 0);
1320596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
13210883ac03SUlf Hansson 	genpd_unlock(genpd);
1322596ba34bSRafael J. Wysocki 
132317218e00SRafael J. Wysocki 	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
132417218e00SRafael J. Wysocki 	    !pm_runtime_status_suspended(dev)) {
132517218e00SRafael J. Wysocki 		ret = genpd_start_dev(genpd, dev);
132610da6542SMikko Perttunen 		if (ret)
132710da6542SMikko Perttunen 			return ret;
1328a935424bSUlf Hansson 	}
132910da6542SMikko Perttunen 
1330a935424bSUlf Hansson 	return pm_generic_resume_noirq(dev);
1331596ba34bSRafael J. Wysocki }
1332596ba34bSRafael J. Wysocki 
1333596ba34bSRafael J. Wysocki /**
1334d9cc34feSShawn Guo  * genpd_resume_noirq - Start of resume of device in an I/O PM domain.
1335d9cc34feSShawn Guo  * @dev: Device to resume.
1336d9cc34feSShawn Guo  *
1337d9cc34feSShawn Guo  * Restore power to the device's PM domain, if necessary, and start the device.
1338d9cc34feSShawn Guo  */
genpd_resume_noirq(struct device * dev)1339d9cc34feSShawn Guo static int genpd_resume_noirq(struct device *dev)
1340d9cc34feSShawn Guo {
1341d9cc34feSShawn Guo 	dev_dbg(dev, "%s()\n", __func__);
1342d9cc34feSShawn Guo 
1343d9cc34feSShawn Guo 	return genpd_finish_resume(dev, pm_generic_resume_noirq);
1344d9cc34feSShawn Guo }
1345d9cc34feSShawn Guo 
1346d9cc34feSShawn Guo /**
13479e9704eaSUlf Hansson  * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
1348596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
1349596ba34bSRafael J. Wysocki  *
1350596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
1351596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
1352596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
1353596ba34bSRafael J. Wysocki  * devices.
1354596ba34bSRafael J. Wysocki  */
genpd_freeze_noirq(struct device * dev)13559e9704eaSUlf Hansson static int genpd_freeze_noirq(struct device *dev)
1356596ba34bSRafael J. Wysocki {
1357596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1358596ba34bSRafael J. Wysocki 
1359ebb486beSShawn Guo 	return genpd_finish_suspend(dev,
1360ebb486beSShawn Guo 				    pm_generic_freeze_noirq,
1361ebb486beSShawn Guo 				    pm_generic_thaw_noirq);
1362596ba34bSRafael J. Wysocki }
1363596ba34bSRafael J. Wysocki 
1364596ba34bSRafael J. Wysocki /**
13659e9704eaSUlf Hansson  * genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
1366596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
1367596ba34bSRafael J. Wysocki  *
13680496c8aeSRafael J. Wysocki  * Start the device, unless power has been removed from the domain already
13690496c8aeSRafael J. Wysocki  * before the system transition.
1370596ba34bSRafael J. Wysocki  */
genpd_thaw_noirq(struct device * dev)13719e9704eaSUlf Hansson static int genpd_thaw_noirq(struct device *dev)
1372596ba34bSRafael J. Wysocki {
1373596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1374596ba34bSRafael J. Wysocki 
1375ebb486beSShawn Guo 	return genpd_finish_resume(dev, pm_generic_thaw_noirq);
137610da6542SMikko Perttunen }
137710da6542SMikko Perttunen 
137810da6542SMikko Perttunen /**
13799e9704eaSUlf Hansson  * genpd_poweroff_noirq - Completion of hibernation of device in an
138010da6542SMikko Perttunen  *   I/O PM domain.
138110da6542SMikko Perttunen  * @dev: Device to poweroff.
138210da6542SMikko Perttunen  *
138310da6542SMikko Perttunen  * Stop the device and remove power from the domain if all devices in it have
138410da6542SMikko Perttunen  * been stopped.
138510da6542SMikko Perttunen  */
genpd_poweroff_noirq(struct device * dev)13869e9704eaSUlf Hansson static int genpd_poweroff_noirq(struct device *dev)
138710da6542SMikko Perttunen {
138810da6542SMikko Perttunen 	dev_dbg(dev, "%s()\n", __func__);
138910da6542SMikko Perttunen 
1390615db6d9SShawn Guo 	return genpd_finish_suspend(dev,
1391615db6d9SShawn Guo 				    pm_generic_poweroff_noirq,
1392615db6d9SShawn Guo 				    pm_generic_restore_noirq);
139310da6542SMikko Perttunen }
139410da6542SMikko Perttunen 
13950496c8aeSRafael J. Wysocki /**
13969e9704eaSUlf Hansson  * genpd_restore_noirq - Start of restore of device in an I/O PM domain.
1397596ba34bSRafael J. Wysocki  * @dev: Device to resume.
1398596ba34bSRafael J. Wysocki  *
13990496c8aeSRafael J. Wysocki  * Make sure the domain will be in the same power state as before the
14000496c8aeSRafael J. Wysocki  * hibernation the system is resuming from and start the device if necessary.
1401596ba34bSRafael J. Wysocki  */
genpd_restore_noirq(struct device * dev)14029e9704eaSUlf Hansson static int genpd_restore_noirq(struct device *dev)
1403596ba34bSRafael J. Wysocki {
1404596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1405596ba34bSRafael J. Wysocki 
1406d9cc34feSShawn Guo 	return genpd_finish_resume(dev, pm_generic_restore_noirq);
140710da6542SMikko Perttunen }
140810da6542SMikko Perttunen 
1409596ba34bSRafael J. Wysocki /**
14109e9704eaSUlf Hansson  * genpd_complete - Complete power transition of a device in a power domain.
1411596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
1412596ba34bSRafael J. Wysocki  *
1413596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
1414596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
1415596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
1416596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
1417596ba34bSRafael J. Wysocki  */
genpd_complete(struct device * dev)14189e9704eaSUlf Hansson static void genpd_complete(struct device *dev)
1419596ba34bSRafael J. Wysocki {
1420596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1421596ba34bSRafael J. Wysocki 
1422596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1423596ba34bSRafael J. Wysocki 
1424596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
1425596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
1426596ba34bSRafael J. Wysocki 		return;
1427596ba34bSRafael J. Wysocki 
14284d23a5e8SUlf Hansson 	pm_generic_complete(dev);
14294d23a5e8SUlf Hansson 
143035241d12SLina Iyer 	genpd_lock(genpd);
1431596ba34bSRafael J. Wysocki 
143239dd0f23SUlf Hansson 	genpd->prepared_count--;
14334d23a5e8SUlf Hansson 	if (!genpd->prepared_count)
14344d23a5e8SUlf Hansson 		genpd_queue_power_off_work(genpd);
1435596ba34bSRafael J. Wysocki 
143635241d12SLina Iyer 	genpd_unlock(genpd);
1437596ba34bSRafael J. Wysocki }
1438596ba34bSRafael J. Wysocki 
genpd_switch_state(struct device * dev,bool suspend)1439fc519890SUlf Hansson static void genpd_switch_state(struct device *dev, bool suspend)
144077f827deSRafael J. Wysocki {
144177f827deSRafael J. Wysocki 	struct generic_pm_domain *genpd;
1442b9795a3eSUlf Hansson 	bool use_lock;
144377f827deSRafael J. Wysocki 
1444fe0c2baaSUlf Hansson 	genpd = dev_to_genpd_safe(dev);
1445fe0c2baaSUlf Hansson 	if (!genpd)
144677f827deSRafael J. Wysocki 		return;
144777f827deSRafael J. Wysocki 
1448b9795a3eSUlf Hansson 	use_lock = genpd_is_irq_safe(genpd);
1449b9795a3eSUlf Hansson 
1450b9795a3eSUlf Hansson 	if (use_lock)
1451b9795a3eSUlf Hansson 		genpd_lock(genpd);
1452b9795a3eSUlf Hansson 
145377f827deSRafael J. Wysocki 	if (suspend) {
145477f827deSRafael J. Wysocki 		genpd->suspended_count++;
1455b9795a3eSUlf Hansson 		genpd_sync_power_off(genpd, use_lock, 0);
145677f827deSRafael J. Wysocki 	} else {
1457b9795a3eSUlf Hansson 		genpd_sync_power_on(genpd, use_lock, 0);
145877f827deSRafael J. Wysocki 		genpd->suspended_count--;
145977f827deSRafael J. Wysocki 	}
1460b9795a3eSUlf Hansson 
1461b9795a3eSUlf Hansson 	if (use_lock)
1462b9795a3eSUlf Hansson 		genpd_unlock(genpd);
146377f827deSRafael J. Wysocki }
1464d47e6464SUlf Hansson 
1465fc519890SUlf Hansson /**
1466fc519890SUlf Hansson  * dev_pm_genpd_suspend - Synchronously try to suspend the genpd for @dev
1467fc519890SUlf Hansson  * @dev: The device that is attached to the genpd, that can be suspended.
1468fc519890SUlf Hansson  *
1469fc519890SUlf Hansson  * This routine should typically be called for a device that needs to be
1470b9795a3eSUlf Hansson  * suspended during the syscore suspend phase. It may also be called during
1471b9795a3eSUlf Hansson  * suspend-to-idle to suspend a corresponding CPU device that is attached to a
1472b9795a3eSUlf Hansson  * genpd.
1473fc519890SUlf Hansson  */
dev_pm_genpd_suspend(struct device * dev)1474fc519890SUlf Hansson void dev_pm_genpd_suspend(struct device *dev)
1475d47e6464SUlf Hansson {
1476fc519890SUlf Hansson 	genpd_switch_state(dev, true);
1477d47e6464SUlf Hansson }
1478fc519890SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_suspend);
1479d47e6464SUlf Hansson 
1480fc519890SUlf Hansson /**
1481fc519890SUlf Hansson  * dev_pm_genpd_resume - Synchronously try to resume the genpd for @dev
1482fc519890SUlf Hansson  * @dev: The device that is attached to the genpd, which needs to be resumed.
1483fc519890SUlf Hansson  *
1484fc519890SUlf Hansson  * This routine should typically be called for a device that needs to be resumed
1485b9795a3eSUlf Hansson  * during the syscore resume phase. It may also be called during suspend-to-idle
1486b9795a3eSUlf Hansson  * to resume a corresponding CPU device that is attached to a genpd.
1487fc519890SUlf Hansson  */
dev_pm_genpd_resume(struct device * dev)1488fc519890SUlf Hansson void dev_pm_genpd_resume(struct device *dev)
1489d47e6464SUlf Hansson {
1490fc519890SUlf Hansson 	genpd_switch_state(dev, false);
1491d47e6464SUlf Hansson }
1492fc519890SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_resume);
149377f827deSRafael J. Wysocki 
1494d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */
1495596ba34bSRafael J. Wysocki 
14969e9704eaSUlf Hansson #define genpd_prepare		NULL
14979e9704eaSUlf Hansson #define genpd_suspend_noirq	NULL
14989e9704eaSUlf Hansson #define genpd_resume_noirq	NULL
14999e9704eaSUlf Hansson #define genpd_freeze_noirq	NULL
15009e9704eaSUlf Hansson #define genpd_thaw_noirq	NULL
15019e9704eaSUlf Hansson #define genpd_poweroff_noirq	NULL
15029e9704eaSUlf Hansson #define genpd_restore_noirq	NULL
15039e9704eaSUlf Hansson #define genpd_complete		NULL
1504596ba34bSRafael J. Wysocki 
1505596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1506596ba34bSRafael J. Wysocki 
genpd_alloc_dev_data(struct device * dev,bool has_governor)150766d29d80SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
150866d29d80SUlf Hansson 							   bool has_governor)
15091d5fcfecSRafael J. Wysocki {
15101d5fcfecSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
151166d29d80SUlf Hansson 	struct gpd_timing_data *td;
15123e235685SUlf Hansson 	int ret;
15133e235685SUlf Hansson 
15143e235685SUlf Hansson 	ret = dev_pm_get_subsys_data(dev);
15153e235685SUlf Hansson 	if (ret)
15163e235685SUlf Hansson 		return ERR_PTR(ret);
15171d5fcfecSRafael J. Wysocki 
15181d5fcfecSRafael J. Wysocki 	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
15193e235685SUlf Hansson 	if (!gpd_data) {
15203e235685SUlf Hansson 		ret = -ENOMEM;
15213e235685SUlf Hansson 		goto err_put;
15223e235685SUlf Hansson 	}
15231d5fcfecSRafael J. Wysocki 
1524f104e1e5SUlf Hansson 	gpd_data->base.dev = dev;
1525f104e1e5SUlf Hansson 	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
1526f104e1e5SUlf Hansson 
152766d29d80SUlf Hansson 	/* Allocate data used by a governor. */
152866d29d80SUlf Hansson 	if (has_governor) {
152966d29d80SUlf Hansson 		td = kzalloc(sizeof(*td), GFP_KERNEL);
153066d29d80SUlf Hansson 		if (!td) {
153166d29d80SUlf Hansson 			ret = -ENOMEM;
1532f104e1e5SUlf Hansson 			goto err_free;
1533f104e1e5SUlf Hansson 		}
1534f104e1e5SUlf Hansson 
153566d29d80SUlf Hansson 		td->constraint_changed = true;
153666d29d80SUlf Hansson 		td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
15379c74f2acSUlf Hansson 		td->next_wakeup = KTIME_MAX;
153866d29d80SUlf Hansson 		gpd_data->td = td;
153966d29d80SUlf Hansson 	}
154066d29d80SUlf Hansson 
154166d29d80SUlf Hansson 	spin_lock_irq(&dev->power.lock);
154266d29d80SUlf Hansson 
154366d29d80SUlf Hansson 	if (dev->power.subsys_data->domain_data)
154466d29d80SUlf Hansson 		ret = -EINVAL;
154566d29d80SUlf Hansson 	else
1546f104e1e5SUlf Hansson 		dev->power.subsys_data->domain_data = &gpd_data->base;
1547f104e1e5SUlf Hansson 
1548f104e1e5SUlf Hansson 	spin_unlock_irq(&dev->power.lock);
1549f104e1e5SUlf Hansson 
155066d29d80SUlf Hansson 	if (ret)
155166d29d80SUlf Hansson 		goto err_free;
155266d29d80SUlf Hansson 
15531d5fcfecSRafael J. Wysocki 	return gpd_data;
15543e235685SUlf Hansson 
1555f104e1e5SUlf Hansson  err_free:
155666d29d80SUlf Hansson 	kfree(gpd_data->td);
1557f104e1e5SUlf Hansson 	kfree(gpd_data);
15583e235685SUlf Hansson  err_put:
15593e235685SUlf Hansson 	dev_pm_put_subsys_data(dev);
15603e235685SUlf Hansson 	return ERR_PTR(ret);
15611d5fcfecSRafael J. Wysocki }
15621d5fcfecSRafael J. Wysocki 
genpd_free_dev_data(struct device * dev,struct generic_pm_domain_data * gpd_data)156349d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev,
15641d5fcfecSRafael J. Wysocki 				struct generic_pm_domain_data *gpd_data)
15651d5fcfecSRafael J. Wysocki {
1566f104e1e5SUlf Hansson 	spin_lock_irq(&dev->power.lock);
1567f104e1e5SUlf Hansson 
1568f104e1e5SUlf Hansson 	dev->power.subsys_data->domain_data = NULL;
1569f104e1e5SUlf Hansson 
1570f104e1e5SUlf Hansson 	spin_unlock_irq(&dev->power.lock);
1571f104e1e5SUlf Hansson 
157266d29d80SUlf Hansson 	kfree(gpd_data->td);
15731d5fcfecSRafael J. Wysocki 	kfree(gpd_data);
15743e235685SUlf Hansson 	dev_pm_put_subsys_data(dev);
15751d5fcfecSRafael J. Wysocki }
15761d5fcfecSRafael J. Wysocki 
genpd_update_cpumask(struct generic_pm_domain * genpd,int cpu,bool set,unsigned int depth)1577b24e1965SUlf Hansson static void genpd_update_cpumask(struct generic_pm_domain *genpd,
1578eb594b73SUlf Hansson 				 int cpu, bool set, unsigned int depth)
1579eb594b73SUlf Hansson {
1580eb594b73SUlf Hansson 	struct gpd_link *link;
1581eb594b73SUlf Hansson 
1582eb594b73SUlf Hansson 	if (!genpd_is_cpu_domain(genpd))
1583eb594b73SUlf Hansson 		return;
1584eb594b73SUlf Hansson 
15858d87ae48SKees Cook 	list_for_each_entry(link, &genpd->child_links, child_node) {
15868d87ae48SKees Cook 		struct generic_pm_domain *parent = link->parent;
1587eb594b73SUlf Hansson 
15888d87ae48SKees Cook 		genpd_lock_nested(parent, depth + 1);
15898d87ae48SKees Cook 		genpd_update_cpumask(parent, cpu, set, depth + 1);
15908d87ae48SKees Cook 		genpd_unlock(parent);
1591eb594b73SUlf Hansson 	}
1592eb594b73SUlf Hansson 
1593eb594b73SUlf Hansson 	if (set)
1594eb594b73SUlf Hansson 		cpumask_set_cpu(cpu, genpd->cpus);
1595eb594b73SUlf Hansson 	else
1596eb594b73SUlf Hansson 		cpumask_clear_cpu(cpu, genpd->cpus);
1597eb594b73SUlf Hansson }
1598eb594b73SUlf Hansson 
genpd_set_cpumask(struct generic_pm_domain * genpd,int cpu)1599b24e1965SUlf Hansson static void genpd_set_cpumask(struct generic_pm_domain *genpd, int cpu)
1600b24e1965SUlf Hansson {
1601b24e1965SUlf Hansson 	if (cpu >= 0)
1602b24e1965SUlf Hansson 		genpd_update_cpumask(genpd, cpu, true, 0);
1603b24e1965SUlf Hansson }
1604b24e1965SUlf Hansson 
genpd_clear_cpumask(struct generic_pm_domain * genpd,int cpu)1605b24e1965SUlf Hansson static void genpd_clear_cpumask(struct generic_pm_domain *genpd, int cpu)
1606b24e1965SUlf Hansson {
1607b24e1965SUlf Hansson 	if (cpu >= 0)
1608b24e1965SUlf Hansson 		genpd_update_cpumask(genpd, cpu, false, 0);
1609b24e1965SUlf Hansson }
1610b24e1965SUlf Hansson 
genpd_get_cpu(struct generic_pm_domain * genpd,struct device * dev)1611b24e1965SUlf Hansson static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev)
1612eb594b73SUlf Hansson {
1613eb594b73SUlf Hansson 	int cpu;
1614eb594b73SUlf Hansson 
1615eb594b73SUlf Hansson 	if (!genpd_is_cpu_domain(genpd))
1616b24e1965SUlf Hansson 		return -1;
1617eb594b73SUlf Hansson 
1618eb594b73SUlf Hansson 	for_each_possible_cpu(cpu) {
1619b24e1965SUlf Hansson 		if (get_cpu_device(cpu) == dev)
1620b24e1965SUlf Hansson 			return cpu;
1621eb594b73SUlf Hansson 	}
1622eb594b73SUlf Hansson 
1623b24e1965SUlf Hansson 	return -1;
1624eb594b73SUlf Hansson }
1625eb594b73SUlf Hansson 
genpd_add_device(struct generic_pm_domain * genpd,struct device * dev,struct device * base_dev)1626f9ccd7c3SUlf Hansson static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1627f9ccd7c3SUlf Hansson 			    struct device *base_dev)
1628f721889fSRafael J. Wysocki {
1629f38d1a6dSUlf Hansson 	struct genpd_governor_data *gd = genpd->gd;
1630c0356db7SUlf Hansson 	struct generic_pm_domain_data *gpd_data;
1631f9ccd7c3SUlf Hansson 	int ret;
1632f721889fSRafael J. Wysocki 
1633f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1634f721889fSRafael J. Wysocki 
1635f38d1a6dSUlf Hansson 	gpd_data = genpd_alloc_dev_data(dev, gd);
16363e235685SUlf Hansson 	if (IS_ERR(gpd_data))
16373e235685SUlf Hansson 		return PTR_ERR(gpd_data);
16386ff7bb0dSRafael J. Wysocki 
1639f9ccd7c3SUlf Hansson 	gpd_data->cpu = genpd_get_cpu(genpd, base_dev);
1640b24e1965SUlf Hansson 
1641b472c2faSUlf Hansson 	ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0;
1642b472c2faSUlf Hansson 	if (ret)
1643b472c2faSUlf Hansson 		goto out;
1644d79b6fe1SGeert Uytterhoeven 
16452071ac98SJiada Wang 	genpd_lock(genpd);
16462071ac98SJiada Wang 
1647f9ccd7c3SUlf Hansson 	genpd_set_cpumask(genpd, gpd_data->cpu);
1648975e83cfSSudeep Holla 	dev_pm_domain_set(dev, &genpd->domain);
1649975e83cfSSudeep Holla 
165014b53064SUlf Hansson 	genpd->device_count++;
1651f38d1a6dSUlf Hansson 	if (gd)
1652f38d1a6dSUlf Hansson 		gd->max_off_time_changed = true;
165314b53064SUlf Hansson 
16541d5fcfecSRafael J. Wysocki 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
16556ff7bb0dSRafael J. Wysocki 
165635241d12SLina Iyer 	genpd_unlock(genpd);
16572071ac98SJiada Wang  out:
1658c0356db7SUlf Hansson 	if (ret)
1659c0356db7SUlf Hansson 		genpd_free_dev_data(dev, gpd_data);
1660c0356db7SUlf Hansson 	else
16610b07ee94SViresh Kumar 		dev_pm_qos_add_notifier(dev, &gpd_data->nb,
16620b07ee94SViresh Kumar 					DEV_PM_QOS_RESUME_LATENCY);
16631d5fcfecSRafael J. Wysocki 
1664f721889fSRafael J. Wysocki 	return ret;
1665f721889fSRafael J. Wysocki }
166619efa5ffSJon Hunter 
166719efa5ffSJon Hunter /**
16681a7a6707SUlf Hansson  * pm_genpd_add_device - Add a device to an I/O PM domain.
166919efa5ffSJon Hunter  * @genpd: PM domain to add the device to.
167019efa5ffSJon Hunter  * @dev: Device to be added.
167119efa5ffSJon Hunter  */
pm_genpd_add_device(struct generic_pm_domain * genpd,struct device * dev)16721a7a6707SUlf Hansson int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
167319efa5ffSJon Hunter {
167419efa5ffSJon Hunter 	int ret;
167519efa5ffSJon Hunter 
16764384a70cSUlf Hansson 	if (!genpd || !dev)
16774384a70cSUlf Hansson 		return -EINVAL;
16784384a70cSUlf Hansson 
167919efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
1680f9ccd7c3SUlf Hansson 	ret = genpd_add_device(genpd, dev, dev);
168119efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
168219efa5ffSJon Hunter 
168319efa5ffSJon Hunter 	return ret;
168419efa5ffSJon Hunter }
16851a7a6707SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_add_device);
1686f721889fSRafael J. Wysocki 
genpd_remove_device(struct generic_pm_domain * genpd,struct device * dev)168785168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd,
1688f721889fSRafael J. Wysocki 			       struct device *dev)
1689f721889fSRafael J. Wysocki {
16906ff7bb0dSRafael J. Wysocki 	struct generic_pm_domain_data *gpd_data;
16914605ab65SRafael J. Wysocki 	struct pm_domain_data *pdd;
1692f9ccd7c3SUlf Hansson 	int ret = 0;
1693f721889fSRafael J. Wysocki 
1694f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
1695f721889fSRafael J. Wysocki 
1696c0356db7SUlf Hansson 	pdd = dev->power.subsys_data->domain_data;
1697c0356db7SUlf Hansson 	gpd_data = to_gpd_data(pdd);
16980b07ee94SViresh Kumar 	dev_pm_qos_remove_notifier(dev, &gpd_data->nb,
16990b07ee94SViresh Kumar 				   DEV_PM_QOS_RESUME_LATENCY);
1700c0356db7SUlf Hansson 
170135241d12SLina Iyer 	genpd_lock(genpd);
1702f721889fSRafael J. Wysocki 
1703596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
1704596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
1705596ba34bSRafael J. Wysocki 		goto out;
1706596ba34bSRafael J. Wysocki 	}
1707596ba34bSRafael J. Wysocki 
17086ff7bb0dSRafael J. Wysocki 	genpd->device_count--;
1709f38d1a6dSUlf Hansson 	if (genpd->gd)
1710f38d1a6dSUlf Hansson 		genpd->gd->max_off_time_changed = true;
17116ff7bb0dSRafael J. Wysocki 
1712f9ccd7c3SUlf Hansson 	genpd_clear_cpumask(genpd, gpd_data->cpu);
1713975e83cfSSudeep Holla 	dev_pm_domain_set(dev, NULL);
1714975e83cfSSudeep Holla 
1715efa69025SRafael J. Wysocki 	list_del_init(&pdd->list_node);
17166ff7bb0dSRafael J. Wysocki 
171735241d12SLina Iyer 	genpd_unlock(genpd);
17186ff7bb0dSRafael J. Wysocki 
17192071ac98SJiada Wang 	if (genpd->detach_dev)
17202071ac98SJiada Wang 		genpd->detach_dev(genpd, dev);
17212071ac98SJiada Wang 
172249d400c7SUlf Hansson 	genpd_free_dev_data(dev, gpd_data);
17231d5fcfecSRafael J. Wysocki 
17246ff7bb0dSRafael J. Wysocki 	return 0;
1725f721889fSRafael J. Wysocki 
1726596ba34bSRafael J. Wysocki  out:
172735241d12SLina Iyer 	genpd_unlock(genpd);
17280b07ee94SViresh Kumar 	dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY);
1729f721889fSRafael J. Wysocki 
1730f721889fSRafael J. Wysocki 	return ret;
1731f721889fSRafael J. Wysocki }
173285168d56SUlf Hansson 
173385168d56SUlf Hansson /**
173485168d56SUlf Hansson  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
173585168d56SUlf Hansson  * @dev: Device to be removed.
173685168d56SUlf Hansson  */
pm_genpd_remove_device(struct device * dev)1737924f4486SUlf Hansson int pm_genpd_remove_device(struct device *dev)
173885168d56SUlf Hansson {
1739b3ad17c0SUlf Hansson 	struct generic_pm_domain *genpd = dev_to_genpd_safe(dev);
1740924f4486SUlf Hansson 
1741924f4486SUlf Hansson 	if (!genpd)
174285168d56SUlf Hansson 		return -EINVAL;
174385168d56SUlf Hansson 
174485168d56SUlf Hansson 	return genpd_remove_device(genpd, dev);
174585168d56SUlf Hansson }
174624c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
1747f721889fSRafael J. Wysocki 
1748d4f81383SUlf Hansson /**
1749d4f81383SUlf Hansson  * dev_pm_genpd_add_notifier - Add a genpd power on/off notifier for @dev
1750d4f81383SUlf Hansson  *
1751d4f81383SUlf Hansson  * @dev: Device that should be associated with the notifier
1752d4f81383SUlf Hansson  * @nb: The notifier block to register
1753d4f81383SUlf Hansson  *
1754d4f81383SUlf Hansson  * Users may call this function to add a genpd power on/off notifier for an
1755d4f81383SUlf Hansson  * attached @dev. Only one notifier per device is allowed. The notifier is
1756d4f81383SUlf Hansson  * sent when genpd is powering on/off the PM domain.
1757d4f81383SUlf Hansson  *
1758d4f81383SUlf Hansson  * It is assumed that the user guarantee that the genpd wouldn't be detached
1759d4f81383SUlf Hansson  * while this routine is getting called.
1760d4f81383SUlf Hansson  *
1761d4f81383SUlf Hansson  * Returns 0 on success and negative error values on failures.
1762d4f81383SUlf Hansson  */
dev_pm_genpd_add_notifier(struct device * dev,struct notifier_block * nb)1763d4f81383SUlf Hansson int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb)
1764d4f81383SUlf Hansson {
1765d4f81383SUlf Hansson 	struct generic_pm_domain *genpd;
1766d4f81383SUlf Hansson 	struct generic_pm_domain_data *gpd_data;
1767d4f81383SUlf Hansson 	int ret;
1768d4f81383SUlf Hansson 
1769d4f81383SUlf Hansson 	genpd = dev_to_genpd_safe(dev);
1770d4f81383SUlf Hansson 	if (!genpd)
1771d4f81383SUlf Hansson 		return -ENODEV;
1772d4f81383SUlf Hansson 
1773d4f81383SUlf Hansson 	if (WARN_ON(!dev->power.subsys_data ||
1774d4f81383SUlf Hansson 		     !dev->power.subsys_data->domain_data))
1775d4f81383SUlf Hansson 		return -EINVAL;
1776d4f81383SUlf Hansson 
1777d4f81383SUlf Hansson 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1778d4f81383SUlf Hansson 	if (gpd_data->power_nb)
1779d4f81383SUlf Hansson 		return -EEXIST;
1780d4f81383SUlf Hansson 
1781d4f81383SUlf Hansson 	genpd_lock(genpd);
1782d4f81383SUlf Hansson 	ret = raw_notifier_chain_register(&genpd->power_notifiers, nb);
1783d4f81383SUlf Hansson 	genpd_unlock(genpd);
1784d4f81383SUlf Hansson 
1785d4f81383SUlf Hansson 	if (ret) {
1786d4f81383SUlf Hansson 		dev_warn(dev, "failed to add notifier for PM domain %s\n",
1787d4f81383SUlf Hansson 			 genpd->name);
1788d4f81383SUlf Hansson 		return ret;
1789d4f81383SUlf Hansson 	}
1790d4f81383SUlf Hansson 
1791d4f81383SUlf Hansson 	gpd_data->power_nb = nb;
1792d4f81383SUlf Hansson 	return 0;
1793d4f81383SUlf Hansson }
1794d4f81383SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_add_notifier);
1795d4f81383SUlf Hansson 
1796d4f81383SUlf Hansson /**
1797d4f81383SUlf Hansson  * dev_pm_genpd_remove_notifier - Remove a genpd power on/off notifier for @dev
1798d4f81383SUlf Hansson  *
1799d4f81383SUlf Hansson  * @dev: Device that is associated with the notifier
1800d4f81383SUlf Hansson  *
1801d4f81383SUlf Hansson  * Users may call this function to remove a genpd power on/off notifier for an
1802d4f81383SUlf Hansson  * attached @dev.
1803d4f81383SUlf Hansson  *
1804d4f81383SUlf Hansson  * It is assumed that the user guarantee that the genpd wouldn't be detached
1805d4f81383SUlf Hansson  * while this routine is getting called.
1806d4f81383SUlf Hansson  *
1807d4f81383SUlf Hansson  * Returns 0 on success and negative error values on failures.
1808d4f81383SUlf Hansson  */
dev_pm_genpd_remove_notifier(struct device * dev)1809d4f81383SUlf Hansson int dev_pm_genpd_remove_notifier(struct device *dev)
1810d4f81383SUlf Hansson {
1811d4f81383SUlf Hansson 	struct generic_pm_domain *genpd;
1812d4f81383SUlf Hansson 	struct generic_pm_domain_data *gpd_data;
1813d4f81383SUlf Hansson 	int ret;
1814d4f81383SUlf Hansson 
1815d4f81383SUlf Hansson 	genpd = dev_to_genpd_safe(dev);
1816d4f81383SUlf Hansson 	if (!genpd)
1817d4f81383SUlf Hansson 		return -ENODEV;
1818d4f81383SUlf Hansson 
1819d4f81383SUlf Hansson 	if (WARN_ON(!dev->power.subsys_data ||
1820d4f81383SUlf Hansson 		     !dev->power.subsys_data->domain_data))
1821d4f81383SUlf Hansson 		return -EINVAL;
1822d4f81383SUlf Hansson 
1823d4f81383SUlf Hansson 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1824d4f81383SUlf Hansson 	if (!gpd_data->power_nb)
1825d4f81383SUlf Hansson 		return -ENODEV;
1826d4f81383SUlf Hansson 
1827d4f81383SUlf Hansson 	genpd_lock(genpd);
1828d4f81383SUlf Hansson 	ret = raw_notifier_chain_unregister(&genpd->power_notifiers,
1829d4f81383SUlf Hansson 					    gpd_data->power_nb);
1830d4f81383SUlf Hansson 	genpd_unlock(genpd);
1831d4f81383SUlf Hansson 
1832d4f81383SUlf Hansson 	if (ret) {
1833d4f81383SUlf Hansson 		dev_warn(dev, "failed to remove notifier for PM domain %s\n",
1834d4f81383SUlf Hansson 			 genpd->name);
1835d4f81383SUlf Hansson 		return ret;
1836d4f81383SUlf Hansson 	}
1837d4f81383SUlf Hansson 
1838d4f81383SUlf Hansson 	gpd_data->power_nb = NULL;
1839d4f81383SUlf Hansson 	return 0;
1840d4f81383SUlf Hansson }
1841d4f81383SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_remove_notifier);
1842d4f81383SUlf Hansson 
genpd_add_subdomain(struct generic_pm_domain * genpd,struct generic_pm_domain * subdomain)184319efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd,
1844bc0403ffSRafael J. Wysocki 			       struct generic_pm_domain *subdomain)
1845f721889fSRafael J. Wysocki {
18462547923dSLina Iyer 	struct gpd_link *link, *itr;
1847f721889fSRafael J. Wysocki 	int ret = 0;
1848f721889fSRafael J. Wysocki 
1849fb7268beSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)
1850fb7268beSRafael J. Wysocki 	    || genpd == subdomain)
1851f721889fSRafael J. Wysocki 		return -EINVAL;
1852f721889fSRafael J. Wysocki 
1853d716f479SLina Iyer 	/*
1854d716f479SLina Iyer 	 * If the domain can be powered on/off in an IRQ safe
1855d716f479SLina Iyer 	 * context, ensure that the subdomain can also be
1856d716f479SLina Iyer 	 * powered on/off in that context.
1857d716f479SLina Iyer 	 */
1858d716f479SLina Iyer 	if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) {
185944cae7d5SDan Carpenter 		WARN(1, "Parent %s of subdomain %s must be IRQ safe\n",
1860d716f479SLina Iyer 				genpd->name, subdomain->name);
1861d716f479SLina Iyer 		return -EINVAL;
1862d716f479SLina Iyer 	}
1863d716f479SLina Iyer 
18642547923dSLina Iyer 	link = kzalloc(sizeof(*link), GFP_KERNEL);
18652547923dSLina Iyer 	if (!link)
18662547923dSLina Iyer 		return -ENOMEM;
18672547923dSLina Iyer 
186835241d12SLina Iyer 	genpd_lock(subdomain);
186935241d12SLina Iyer 	genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
1870f721889fSRafael J. Wysocki 
187141e2c8e0SUlf Hansson 	if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) {
1872f721889fSRafael J. Wysocki 		ret = -EINVAL;
1873f721889fSRafael J. Wysocki 		goto out;
1874f721889fSRafael J. Wysocki 	}
1875f721889fSRafael J. Wysocki 
18768d87ae48SKees Cook 	list_for_each_entry(itr, &genpd->parent_links, parent_node) {
18778d87ae48SKees Cook 		if (itr->child == subdomain && itr->parent == genpd) {
1878f721889fSRafael J. Wysocki 			ret = -EINVAL;
1879f721889fSRafael J. Wysocki 			goto out;
1880f721889fSRafael J. Wysocki 		}
1881f721889fSRafael J. Wysocki 	}
1882f721889fSRafael J. Wysocki 
18838d87ae48SKees Cook 	link->parent = genpd;
18848d87ae48SKees Cook 	list_add_tail(&link->parent_node, &genpd->parent_links);
18858d87ae48SKees Cook 	link->child = subdomain;
18868d87ae48SKees Cook 	list_add_tail(&link->child_node, &subdomain->child_links);
188741e2c8e0SUlf Hansson 	if (genpd_status_on(subdomain))
1888c4bb3160SRafael J. Wysocki 		genpd_sd_counter_inc(genpd);
1889f721889fSRafael J. Wysocki 
1890f721889fSRafael J. Wysocki  out:
189135241d12SLina Iyer 	genpd_unlock(genpd);
189235241d12SLina Iyer 	genpd_unlock(subdomain);
18932547923dSLina Iyer 	if (ret)
18942547923dSLina Iyer 		kfree(link);
1895f721889fSRafael J. Wysocki 	return ret;
1896f721889fSRafael J. Wysocki }
189719efa5ffSJon Hunter 
189819efa5ffSJon Hunter /**
189919efa5ffSJon Hunter  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
19008d87ae48SKees Cook  * @genpd: Leader PM domain to add the subdomain to.
190119efa5ffSJon Hunter  * @subdomain: Subdomain to be added.
190219efa5ffSJon Hunter  */
pm_genpd_add_subdomain(struct generic_pm_domain * genpd,struct generic_pm_domain * subdomain)190319efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
190419efa5ffSJon Hunter 			   struct generic_pm_domain *subdomain)
190519efa5ffSJon Hunter {
190619efa5ffSJon Hunter 	int ret;
190719efa5ffSJon Hunter 
190819efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
190919efa5ffSJon Hunter 	ret = genpd_add_subdomain(genpd, subdomain);
191019efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
191119efa5ffSJon Hunter 
191219efa5ffSJon Hunter 	return ret;
191319efa5ffSJon Hunter }
1914d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
1915f721889fSRafael J. Wysocki 
1916f721889fSRafael J. Wysocki /**
1917f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
19188d87ae48SKees Cook  * @genpd: Leader PM domain to remove the subdomain from.
19195063ce15SRafael J. Wysocki  * @subdomain: Subdomain to be removed.
1920f721889fSRafael J. Wysocki  */
pm_genpd_remove_subdomain(struct generic_pm_domain * genpd,struct generic_pm_domain * subdomain)1921f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
19225063ce15SRafael J. Wysocki 			      struct generic_pm_domain *subdomain)
1923f721889fSRafael J. Wysocki {
1924c6e83cacSKrzysztof Kozlowski 	struct gpd_link *l, *link;
1925f721889fSRafael J. Wysocki 	int ret = -EINVAL;
1926f721889fSRafael J. Wysocki 
19275063ce15SRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1928f721889fSRafael J. Wysocki 		return -EINVAL;
1929f721889fSRafael J. Wysocki 
193035241d12SLina Iyer 	genpd_lock(subdomain);
193135241d12SLina Iyer 	genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
1932f721889fSRafael J. Wysocki 
19338d87ae48SKees Cook 	if (!list_empty(&subdomain->parent_links) || subdomain->device_count) {
19347a5bd127SJoe Perches 		pr_warn("%s: unable to remove subdomain %s\n",
19357a5bd127SJoe Perches 			genpd->name, subdomain->name);
193630e7a65bSJon Hunter 		ret = -EBUSY;
193730e7a65bSJon Hunter 		goto out;
193830e7a65bSJon Hunter 	}
193930e7a65bSJon Hunter 
19408d87ae48SKees Cook 	list_for_each_entry_safe(link, l, &genpd->parent_links, parent_node) {
19418d87ae48SKees Cook 		if (link->child != subdomain)
1942f721889fSRafael J. Wysocki 			continue;
1943f721889fSRafael J. Wysocki 
19448d87ae48SKees Cook 		list_del(&link->parent_node);
19458d87ae48SKees Cook 		list_del(&link->child_node);
19465063ce15SRafael J. Wysocki 		kfree(link);
194741e2c8e0SUlf Hansson 		if (genpd_status_on(subdomain))
1948f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
1949f721889fSRafael J. Wysocki 
1950f721889fSRafael J. Wysocki 		ret = 0;
1951f721889fSRafael J. Wysocki 		break;
1952f721889fSRafael J. Wysocki 	}
1953f721889fSRafael J. Wysocki 
195430e7a65bSJon Hunter out:
195535241d12SLina Iyer 	genpd_unlock(genpd);
195635241d12SLina Iyer 	genpd_unlock(subdomain);
1957f721889fSRafael J. Wysocki 
1958f721889fSRafael J. Wysocki 	return ret;
1959f721889fSRafael J. Wysocki }
1960d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
1961f721889fSRafael J. Wysocki 
genpd_free_default_power_state(struct genpd_power_state * states,unsigned int state_count)196249a27e27SUlf Hansson static void genpd_free_default_power_state(struct genpd_power_state *states,
196349a27e27SUlf Hansson 					   unsigned int state_count)
196449a27e27SUlf Hansson {
196549a27e27SUlf Hansson 	kfree(states);
196649a27e27SUlf Hansson }
196749a27e27SUlf Hansson 
genpd_set_default_power_state(struct generic_pm_domain * genpd)196859d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
196959d65b73SLina Iyer {
197059d65b73SLina Iyer 	struct genpd_power_state *state;
197159d65b73SLina Iyer 
197259d65b73SLina Iyer 	state = kzalloc(sizeof(*state), GFP_KERNEL);
197359d65b73SLina Iyer 	if (!state)
197459d65b73SLina Iyer 		return -ENOMEM;
197559d65b73SLina Iyer 
197659d65b73SLina Iyer 	genpd->states = state;
197759d65b73SLina Iyer 	genpd->state_count = 1;
197849a27e27SUlf Hansson 	genpd->free_states = genpd_free_default_power_state;
197959d65b73SLina Iyer 
198059d65b73SLina Iyer 	return 0;
198159d65b73SLina Iyer }
198259d65b73SLina Iyer 
genpd_alloc_data(struct generic_pm_domain * genpd)1983ba43d6dbSUlf Hansson static int genpd_alloc_data(struct generic_pm_domain *genpd)
1984ba43d6dbSUlf Hansson {
1985f38d1a6dSUlf Hansson 	struct genpd_governor_data *gd = NULL;
1986ba43d6dbSUlf Hansson 	int ret;
1987ba43d6dbSUlf Hansson 
1988ba43d6dbSUlf Hansson 	if (genpd_is_cpu_domain(genpd) &&
1989ba43d6dbSUlf Hansson 	    !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL))
1990ba43d6dbSUlf Hansson 		return -ENOMEM;
1991ba43d6dbSUlf Hansson 
1992f38d1a6dSUlf Hansson 	if (genpd->gov) {
1993f38d1a6dSUlf Hansson 		gd = kzalloc(sizeof(*gd), GFP_KERNEL);
1994f38d1a6dSUlf Hansson 		if (!gd) {
1995f38d1a6dSUlf Hansson 			ret = -ENOMEM;
1996f38d1a6dSUlf Hansson 			goto free;
1997f38d1a6dSUlf Hansson 		}
1998f38d1a6dSUlf Hansson 
1999f38d1a6dSUlf Hansson 		gd->max_off_time_ns = -1;
2000f38d1a6dSUlf Hansson 		gd->max_off_time_changed = true;
2001f38d1a6dSUlf Hansson 		gd->next_wakeup = KTIME_MAX;
20021498c503SMaulik Shah 		gd->next_hrtimer = KTIME_MAX;
2003f38d1a6dSUlf Hansson 	}
2004f38d1a6dSUlf Hansson 
2005ba43d6dbSUlf Hansson 	/* Use only one "off" state if there were no states declared */
2006ba43d6dbSUlf Hansson 	if (genpd->state_count == 0) {
2007ba43d6dbSUlf Hansson 		ret = genpd_set_default_power_state(genpd);
2008ba43d6dbSUlf Hansson 		if (ret)
2009ba43d6dbSUlf Hansson 			goto free;
2010ba43d6dbSUlf Hansson 	}
2011ba43d6dbSUlf Hansson 
2012f38d1a6dSUlf Hansson 	genpd->gd = gd;
2013ba43d6dbSUlf Hansson 	return 0;
2014ba43d6dbSUlf Hansson 
2015ba43d6dbSUlf Hansson free:
2016ba43d6dbSUlf Hansson 	if (genpd_is_cpu_domain(genpd))
2017ba43d6dbSUlf Hansson 		free_cpumask_var(genpd->cpus);
2018f38d1a6dSUlf Hansson 	kfree(gd);
2019ba43d6dbSUlf Hansson 	return ret;
2020ba43d6dbSUlf Hansson }
2021ba43d6dbSUlf Hansson 
genpd_free_data(struct generic_pm_domain * genpd)2022ba43d6dbSUlf Hansson static void genpd_free_data(struct generic_pm_domain *genpd)
2023ba43d6dbSUlf Hansson {
2024ba43d6dbSUlf Hansson 	if (genpd_is_cpu_domain(genpd))
2025ba43d6dbSUlf Hansson 		free_cpumask_var(genpd->cpus);
2026ba43d6dbSUlf Hansson 	if (genpd->free_states)
2027ba43d6dbSUlf Hansson 		genpd->free_states(genpd->states, genpd->state_count);
2028f38d1a6dSUlf Hansson 	kfree(genpd->gd);
2029ba43d6dbSUlf Hansson }
2030ba43d6dbSUlf Hansson 
genpd_lock_init(struct generic_pm_domain * genpd)2031d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd)
2032d716f479SLina Iyer {
2033d716f479SLina Iyer 	if (genpd->flags & GENPD_FLAG_IRQ_SAFE) {
2034d716f479SLina Iyer 		spin_lock_init(&genpd->slock);
2035d716f479SLina Iyer 		genpd->lock_ops = &genpd_spin_ops;
2036d716f479SLina Iyer 	} else {
2037d716f479SLina Iyer 		mutex_init(&genpd->mlock);
2038d716f479SLina Iyer 		genpd->lock_ops = &genpd_mtx_ops;
2039d716f479SLina Iyer 	}
2040d716f479SLina Iyer }
2041d716f479SLina Iyer 
2042d23b9b00SRafael J. Wysocki /**
2043f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
2044f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
2045f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
2046f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
20477eb231c3SUlf Hansson  *
20487eb231c3SUlf Hansson  * Returns 0 on successful initialization, else a negative error code.
2049f721889fSRafael J. Wysocki  */
pm_genpd_init(struct generic_pm_domain * genpd,struct dev_power_governor * gov,bool is_off)20507eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd,
2051f721889fSRafael J. Wysocki 		  struct dev_power_governor *gov, bool is_off)
2052f721889fSRafael J. Wysocki {
205359d65b73SLina Iyer 	int ret;
205459d65b73SLina Iyer 
2055f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
20567eb231c3SUlf Hansson 		return -EINVAL;
2057f721889fSRafael J. Wysocki 
20588d87ae48SKees Cook 	INIT_LIST_HEAD(&genpd->parent_links);
20598d87ae48SKees Cook 	INIT_LIST_HEAD(&genpd->child_links);
2060f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
2061d4f81383SUlf Hansson 	RAW_INIT_NOTIFIER_HEAD(&genpd->power_notifiers);
2062d716f479SLina Iyer 	genpd_lock_init(genpd);
2063f721889fSRafael J. Wysocki 	genpd->gov = gov;
2064f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
2065c4bb3160SRafael J. Wysocki 	atomic_set(&genpd->sd_count, 0);
206649f618e1SUlf Hansson 	genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
2067596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
2068de0aa06dSJon Hunter 	genpd->provider = NULL;
2069de0aa06dSJon Hunter 	genpd->has_provider = false;
2070bd40cbb0SUlf Hansson 	genpd->accounting_time = ktime_get_mono_fast_ns();
2071795bd2e7SUlf Hansson 	genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
2072795bd2e7SUlf Hansson 	genpd->domain.ops.runtime_resume = genpd_runtime_resume;
20739e9704eaSUlf Hansson 	genpd->domain.ops.prepare = genpd_prepare;
20749e9704eaSUlf Hansson 	genpd->domain.ops.suspend_noirq = genpd_suspend_noirq;
20759e9704eaSUlf Hansson 	genpd->domain.ops.resume_noirq = genpd_resume_noirq;
20769e9704eaSUlf Hansson 	genpd->domain.ops.freeze_noirq = genpd_freeze_noirq;
20779e9704eaSUlf Hansson 	genpd->domain.ops.thaw_noirq = genpd_thaw_noirq;
20789e9704eaSUlf Hansson 	genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq;
20799e9704eaSUlf Hansson 	genpd->domain.ops.restore_noirq = genpd_restore_noirq;
20809e9704eaSUlf Hansson 	genpd->domain.ops.complete = genpd_complete;
2081ea71c596SUlf Hansson 	genpd->domain.start = genpd_dev_pm_start;
2082c11f6f5bSUlf Hansson 
2083c11f6f5bSUlf Hansson 	if (genpd->flags & GENPD_FLAG_PM_CLK) {
2084c11f6f5bSUlf Hansson 		genpd->dev_ops.stop = pm_clk_suspend;
2085c11f6f5bSUlf Hansson 		genpd->dev_ops.start = pm_clk_resume;
2086c11f6f5bSUlf Hansson 	}
2087c11f6f5bSUlf Hansson 
208827656dcdSUlf Hansson 	/* The always-on governor works better with the corresponding flag. */
208927656dcdSUlf Hansson 	if (gov == &pm_domain_always_on_gov)
209027656dcdSUlf Hansson 		genpd->flags |= GENPD_FLAG_RPM_ALWAYS_ON;
209127656dcdSUlf Hansson 
2092ffaa42e8SUlf Hansson 	/* Always-on domains must be powered on at initialization. */
2093ed61e18aSLeonard Crestez 	if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) &&
2094129b60c9SJohan Hovold 			!genpd_status_on(genpd)) {
2095129b60c9SJohan Hovold 		pr_err("always-on PM domain %s is not on\n", genpd->name);
2096ffaa42e8SUlf Hansson 		return -EINVAL;
2097129b60c9SJohan Hovold 	}
2098ffaa42e8SUlf Hansson 
2099ba43d6dbSUlf Hansson 	/* Multiple states but no governor doesn't make sense. */
2100ba43d6dbSUlf Hansson 	if (!gov && genpd->state_count > 1)
21012c9b7f87SUlf Hansson 		pr_warn("%s: no governor for states\n", genpd->name);
2102ba43d6dbSUlf Hansson 
2103ba43d6dbSUlf Hansson 	ret = genpd_alloc_data(genpd);
2104ba43d6dbSUlf Hansson 	if (ret)
2105ba43d6dbSUlf Hansson 		return ret;
2106fc5cbf0cSAxel Haslam 
2107401ea157SViresh Kumar 	device_initialize(&genpd->dev);
2108401ea157SViresh Kumar 	dev_set_name(&genpd->dev, "%s", genpd->name);
2109401ea157SViresh Kumar 
21105125bbf3SRafael J. Wysocki 	mutex_lock(&gpd_list_lock);
21115125bbf3SRafael J. Wysocki 	list_add(&genpd->gpd_list_node, &gpd_list);
21125125bbf3SRafael J. Wysocki 	mutex_unlock(&gpd_list_lock);
211340ba55e4SStephen Boyd 	genpd_debug_add(genpd);
21147eb231c3SUlf Hansson 
21157eb231c3SUlf Hansson 	return 0;
21165125bbf3SRafael J. Wysocki }
2117be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init);
2118aa42240aSTomasz Figa 
genpd_remove(struct generic_pm_domain * genpd)21193fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd)
21203fe57710SJon Hunter {
21213fe57710SJon Hunter 	struct gpd_link *l, *link;
21223fe57710SJon Hunter 
21233fe57710SJon Hunter 	if (IS_ERR_OR_NULL(genpd))
21243fe57710SJon Hunter 		return -EINVAL;
21253fe57710SJon Hunter 
212635241d12SLina Iyer 	genpd_lock(genpd);
21273fe57710SJon Hunter 
21283fe57710SJon Hunter 	if (genpd->has_provider) {
212935241d12SLina Iyer 		genpd_unlock(genpd);
21303fe57710SJon Hunter 		pr_err("Provider present, unable to remove %s\n", genpd->name);
21313fe57710SJon Hunter 		return -EBUSY;
21323fe57710SJon Hunter 	}
21333fe57710SJon Hunter 
21348d87ae48SKees Cook 	if (!list_empty(&genpd->parent_links) || genpd->device_count) {
213535241d12SLina Iyer 		genpd_unlock(genpd);
21363fe57710SJon Hunter 		pr_err("%s: unable to remove %s\n", __func__, genpd->name);
21373fe57710SJon Hunter 		return -EBUSY;
21383fe57710SJon Hunter 	}
21393fe57710SJon Hunter 
21408d87ae48SKees Cook 	list_for_each_entry_safe(link, l, &genpd->child_links, child_node) {
21418d87ae48SKees Cook 		list_del(&link->parent_node);
21428d87ae48SKees Cook 		list_del(&link->child_node);
21433fe57710SJon Hunter 		kfree(link);
21443fe57710SJon Hunter 	}
21453fe57710SJon Hunter 
21463fe57710SJon Hunter 	list_del(&genpd->gpd_list_node);
214735241d12SLina Iyer 	genpd_unlock(genpd);
2148f6bfe8b5SShawn Guo 	genpd_debug_remove(genpd);
21493fe57710SJon Hunter 	cancel_work_sync(&genpd->power_off_work);
2150ba43d6dbSUlf Hansson 	genpd_free_data(genpd);
215149a27e27SUlf Hansson 
21523fe57710SJon Hunter 	pr_debug("%s: removed %s\n", __func__, genpd->name);
21533fe57710SJon Hunter 
21543fe57710SJon Hunter 	return 0;
21553fe57710SJon Hunter }
21563fe57710SJon Hunter 
21573fe57710SJon Hunter /**
21583fe57710SJon Hunter  * pm_genpd_remove - Remove a generic I/O PM domain
21593fe57710SJon Hunter  * @genpd: Pointer to PM domain that is to be removed.
21603fe57710SJon Hunter  *
21613fe57710SJon Hunter  * To remove the PM domain, this function:
21623fe57710SJon Hunter  *  - Removes the PM domain as a subdomain to any parent domains,
21633fe57710SJon Hunter  *    if it was added.
21643fe57710SJon Hunter  *  - Removes the PM domain from the list of registered PM domains.
21653fe57710SJon Hunter  *
21663fe57710SJon Hunter  * The PM domain will only be removed, if the associated provider has
21673fe57710SJon Hunter  * been removed, it is not a parent to any other PM domain and has no
21683fe57710SJon Hunter  * devices associated with it.
21693fe57710SJon Hunter  */
pm_genpd_remove(struct generic_pm_domain * genpd)21703fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd)
21713fe57710SJon Hunter {
21723fe57710SJon Hunter 	int ret;
21733fe57710SJon Hunter 
21743fe57710SJon Hunter 	mutex_lock(&gpd_list_lock);
21753fe57710SJon Hunter 	ret = genpd_remove(genpd);
21763fe57710SJon Hunter 	mutex_unlock(&gpd_list_lock);
21773fe57710SJon Hunter 
21783fe57710SJon Hunter 	return ret;
21793fe57710SJon Hunter }
21803fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove);
21813fe57710SJon Hunter 
2182aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
2183892ebdccSJon Hunter 
2184aa42240aSTomasz Figa /*
2185aa42240aSTomasz Figa  * Device Tree based PM domain providers.
2186aa42240aSTomasz Figa  *
2187aa42240aSTomasz Figa  * The code below implements generic device tree based PM domain providers that
2188aa42240aSTomasz Figa  * bind device tree nodes with generic PM domains registered in the system.
2189aa42240aSTomasz Figa  *
2190aa42240aSTomasz Figa  * Any driver that registers generic PM domains and needs to support binding of
2191aa42240aSTomasz Figa  * devices to these domains is supposed to register a PM domain provider, which
2192aa42240aSTomasz Figa  * maps a PM domain specifier retrieved from the device tree to a PM domain.
2193aa42240aSTomasz Figa  *
2194aa42240aSTomasz Figa  * Two simple mapping functions have been provided for convenience:
2195892ebdccSJon Hunter  *  - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
2196892ebdccSJon Hunter  *  - genpd_xlate_onecell() for mapping of multiple PM domains per node by
2197aa42240aSTomasz Figa  *    index.
2198aa42240aSTomasz Figa  */
2199aa42240aSTomasz Figa 
2200aa42240aSTomasz Figa /**
2201aa42240aSTomasz Figa  * struct of_genpd_provider - PM domain provider registration structure
2202aa42240aSTomasz Figa  * @link: Entry in global list of PM domain providers
2203aa42240aSTomasz Figa  * @node: Pointer to device tree node of PM domain provider
2204aa42240aSTomasz Figa  * @xlate: Provider-specific xlate callback mapping a set of specifier cells
2205aa42240aSTomasz Figa  *         into a PM domain.
2206aa42240aSTomasz Figa  * @data: context pointer to be passed into @xlate callback
2207aa42240aSTomasz Figa  */
2208aa42240aSTomasz Figa struct of_genpd_provider {
2209aa42240aSTomasz Figa 	struct list_head link;
2210aa42240aSTomasz Figa 	struct device_node *node;
2211aa42240aSTomasz Figa 	genpd_xlate_t xlate;
2212aa42240aSTomasz Figa 	void *data;
2213aa42240aSTomasz Figa };
2214aa42240aSTomasz Figa 
2215aa42240aSTomasz Figa /* List of registered PM domain providers. */
2216aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers);
2217aa42240aSTomasz Figa /* Mutex to protect the list above. */
2218aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex);
2219aa42240aSTomasz Figa 
2220aa42240aSTomasz Figa /**
2221892ebdccSJon Hunter  * genpd_xlate_simple() - Xlate function for direct node-domain mapping
2222aa42240aSTomasz Figa  * @genpdspec: OF phandle args to map into a PM domain
2223aa42240aSTomasz Figa  * @data: xlate function private data - pointer to struct generic_pm_domain
2224aa42240aSTomasz Figa  *
2225aa42240aSTomasz Figa  * This is a generic xlate function that can be used to model PM domains that
2226aa42240aSTomasz Figa  * have their own device tree nodes. The private data of xlate function needs
2227aa42240aSTomasz Figa  * to be a valid pointer to struct generic_pm_domain.
2228aa42240aSTomasz Figa  */
genpd_xlate_simple(struct of_phandle_args * genpdspec,void * data)2229892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple(
2230aa42240aSTomasz Figa 					struct of_phandle_args *genpdspec,
2231aa42240aSTomasz Figa 					void *data)
2232aa42240aSTomasz Figa {
2233aa42240aSTomasz Figa 	return data;
2234aa42240aSTomasz Figa }
2235aa42240aSTomasz Figa 
2236aa42240aSTomasz Figa /**
2237892ebdccSJon Hunter  * genpd_xlate_onecell() - Xlate function using a single index.
2238aa42240aSTomasz Figa  * @genpdspec: OF phandle args to map into a PM domain
2239aa42240aSTomasz Figa  * @data: xlate function private data - pointer to struct genpd_onecell_data
2240aa42240aSTomasz Figa  *
2241aa42240aSTomasz Figa  * This is a generic xlate function that can be used to model simple PM domain
2242aa42240aSTomasz Figa  * controllers that have one device tree node and provide multiple PM domains.
2243aa42240aSTomasz Figa  * A single cell is used as an index into an array of PM domains specified in
2244aa42240aSTomasz Figa  * the genpd_onecell_data struct when registering the provider.
2245aa42240aSTomasz Figa  */
genpd_xlate_onecell(struct of_phandle_args * genpdspec,void * data)2246892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell(
2247aa42240aSTomasz Figa 					struct of_phandle_args *genpdspec,
2248aa42240aSTomasz Figa 					void *data)
2249aa42240aSTomasz Figa {
2250aa42240aSTomasz Figa 	struct genpd_onecell_data *genpd_data = data;
2251aa42240aSTomasz Figa 	unsigned int idx = genpdspec->args[0];
2252aa42240aSTomasz Figa 
2253aa42240aSTomasz Figa 	if (genpdspec->args_count != 1)
2254aa42240aSTomasz Figa 		return ERR_PTR(-EINVAL);
2255aa42240aSTomasz Figa 
2256aa42240aSTomasz Figa 	if (idx >= genpd_data->num_domains) {
2257aa42240aSTomasz Figa 		pr_err("%s: invalid domain index %u\n", __func__, idx);
2258aa42240aSTomasz Figa 		return ERR_PTR(-EINVAL);
2259aa42240aSTomasz Figa 	}
2260aa42240aSTomasz Figa 
2261aa42240aSTomasz Figa 	if (!genpd_data->domains[idx])
2262aa42240aSTomasz Figa 		return ERR_PTR(-ENOENT);
2263aa42240aSTomasz Figa 
2264aa42240aSTomasz Figa 	return genpd_data->domains[idx];
2265aa42240aSTomasz Figa }
2266aa42240aSTomasz Figa 
2267aa42240aSTomasz Figa /**
2268892ebdccSJon Hunter  * genpd_add_provider() - Register a PM domain provider for a node
2269aa42240aSTomasz Figa  * @np: Device node pointer associated with the PM domain provider.
2270aa42240aSTomasz Figa  * @xlate: Callback for decoding PM domain from phandle arguments.
2271aa42240aSTomasz Figa  * @data: Context pointer for @xlate callback.
2272aa42240aSTomasz Figa  */
genpd_add_provider(struct device_node * np,genpd_xlate_t xlate,void * data)2273892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
2274aa42240aSTomasz Figa 			      void *data)
2275aa42240aSTomasz Figa {
2276aa42240aSTomasz Figa 	struct of_genpd_provider *cp;
2277aa42240aSTomasz Figa 
2278aa42240aSTomasz Figa 	cp = kzalloc(sizeof(*cp), GFP_KERNEL);
2279aa42240aSTomasz Figa 	if (!cp)
2280aa42240aSTomasz Figa 		return -ENOMEM;
2281aa42240aSTomasz Figa 
2282aa42240aSTomasz Figa 	cp->node = of_node_get(np);
2283aa42240aSTomasz Figa 	cp->data = data;
2284aa42240aSTomasz Figa 	cp->xlate = xlate;
2285bab2d712SSaravana Kannan 	fwnode_dev_initialized(&np->fwnode, true);
2286aa42240aSTomasz Figa 
2287aa42240aSTomasz Figa 	mutex_lock(&of_genpd_mutex);
2288aa42240aSTomasz Figa 	list_add(&cp->link, &of_genpd_providers);
2289aa42240aSTomasz Figa 	mutex_unlock(&of_genpd_mutex);
2290ea11e94bSRob Herring 	pr_debug("Added domain provider from %pOF\n", np);
2291aa42240aSTomasz Figa 
2292aa42240aSTomasz Figa 	return 0;
2293aa42240aSTomasz Figa }
2294892ebdccSJon Hunter 
genpd_present(const struct generic_pm_domain * genpd)2295fe0c2baaSUlf Hansson static bool genpd_present(const struct generic_pm_domain *genpd)
2296fe0c2baaSUlf Hansson {
229740ba55e4SStephen Boyd 	bool ret = false;
2298fe0c2baaSUlf Hansson 	const struct generic_pm_domain *gpd;
2299fe0c2baaSUlf Hansson 
230040ba55e4SStephen Boyd 	mutex_lock(&gpd_list_lock);
230140ba55e4SStephen Boyd 	list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
230240ba55e4SStephen Boyd 		if (gpd == genpd) {
230340ba55e4SStephen Boyd 			ret = true;
230440ba55e4SStephen Boyd 			break;
230540ba55e4SStephen Boyd 		}
230640ba55e4SStephen Boyd 	}
230740ba55e4SStephen Boyd 	mutex_unlock(&gpd_list_lock);
230840ba55e4SStephen Boyd 
230940ba55e4SStephen Boyd 	return ret;
2310fe0c2baaSUlf Hansson }
2311fe0c2baaSUlf Hansson 
2312892ebdccSJon Hunter /**
2313892ebdccSJon Hunter  * of_genpd_add_provider_simple() - Register a simple PM domain provider
2314892ebdccSJon Hunter  * @np: Device node pointer associated with the PM domain provider.
2315892ebdccSJon Hunter  * @genpd: Pointer to PM domain associated with the PM domain provider.
2316892ebdccSJon Hunter  */
of_genpd_add_provider_simple(struct device_node * np,struct generic_pm_domain * genpd)2317892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np,
2318892ebdccSJon Hunter 				 struct generic_pm_domain *genpd)
2319892ebdccSJon Hunter {
232040ba55e4SStephen Boyd 	int ret;
23210159ec67SJon Hunter 
23220159ec67SJon Hunter 	if (!np || !genpd)
23230159ec67SJon Hunter 		return -EINVAL;
23240159ec67SJon Hunter 
23256a0ae73dSViresh Kumar 	if (!genpd_present(genpd))
232640ba55e4SStephen Boyd 		return -EINVAL;
23276a0ae73dSViresh Kumar 
23286a0ae73dSViresh Kumar 	genpd->dev.of_node = np;
23296a0ae73dSViresh Kumar 
23306a0ae73dSViresh Kumar 	/* Parse genpd OPP table */
23316a0ae73dSViresh Kumar 	if (genpd->set_performance_state) {
23326a0ae73dSViresh Kumar 		ret = dev_pm_opp_of_add_table(&genpd->dev);
23339a6582b8SAhmad Fatoum 		if (ret)
23349a6582b8SAhmad Fatoum 			return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
23351067ae3eSViresh Kumar 
23361067ae3eSViresh Kumar 		/*
23371067ae3eSViresh Kumar 		 * Save table for faster processing while setting performance
23381067ae3eSViresh Kumar 		 * state.
23391067ae3eSViresh Kumar 		 */
23401067ae3eSViresh Kumar 		genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
2341dd461cd9SStephan Gerhold 		WARN_ON(IS_ERR(genpd->opp_table));
23428ce95844SViresh Kumar 	}
2343de0aa06dSJon Hunter 
23446a0ae73dSViresh Kumar 	ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
23456a0ae73dSViresh Kumar 	if (ret) {
23461067ae3eSViresh Kumar 		if (genpd->set_performance_state) {
23471067ae3eSViresh Kumar 			dev_pm_opp_put_opp_table(genpd->opp_table);
23486a0ae73dSViresh Kumar 			dev_pm_opp_of_remove_table(&genpd->dev);
23491067ae3eSViresh Kumar 		}
23506a0ae73dSViresh Kumar 
235140ba55e4SStephen Boyd 		return ret;
23526a0ae73dSViresh Kumar 	}
23536a0ae73dSViresh Kumar 
23546a0ae73dSViresh Kumar 	genpd->provider = &np->fwnode;
23556a0ae73dSViresh Kumar 	genpd->has_provider = true;
23566a0ae73dSViresh Kumar 
235740ba55e4SStephen Boyd 	return 0;
2358892ebdccSJon Hunter }
2359892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
2360892ebdccSJon Hunter 
2361892ebdccSJon Hunter /**
2362892ebdccSJon Hunter  * of_genpd_add_provider_onecell() - Register a onecell PM domain provider
2363892ebdccSJon Hunter  * @np: Device node pointer associated with the PM domain provider.
2364892ebdccSJon Hunter  * @data: Pointer to the data associated with the PM domain provider.
2365892ebdccSJon Hunter  */
of_genpd_add_provider_onecell(struct device_node * np,struct genpd_onecell_data * data)2366892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np,
2367892ebdccSJon Hunter 				  struct genpd_onecell_data *data)
2368892ebdccSJon Hunter {
23696a0ae73dSViresh Kumar 	struct generic_pm_domain *genpd;
23700159ec67SJon Hunter 	unsigned int i;
2371de0aa06dSJon Hunter 	int ret = -EINVAL;
23720159ec67SJon Hunter 
23730159ec67SJon Hunter 	if (!np || !data)
23740159ec67SJon Hunter 		return -EINVAL;
23750159ec67SJon Hunter 
237640845524SThierry Reding 	if (!data->xlate)
237740845524SThierry Reding 		data->xlate = genpd_xlate_onecell;
237840845524SThierry Reding 
23790159ec67SJon Hunter 	for (i = 0; i < data->num_domains; i++) {
23806a0ae73dSViresh Kumar 		genpd = data->domains[i];
23816a0ae73dSViresh Kumar 
23826a0ae73dSViresh Kumar 		if (!genpd)
2383609bed67STomeu Vizoso 			continue;
23846a0ae73dSViresh Kumar 		if (!genpd_present(genpd))
2385de0aa06dSJon Hunter 			goto error;
2386de0aa06dSJon Hunter 
23876a0ae73dSViresh Kumar 		genpd->dev.of_node = np;
23886a0ae73dSViresh Kumar 
23896a0ae73dSViresh Kumar 		/* Parse genpd OPP table */
23906a0ae73dSViresh Kumar 		if (genpd->set_performance_state) {
23916a0ae73dSViresh Kumar 			ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
23926a0ae73dSViresh Kumar 			if (ret) {
23939a6582b8SAhmad Fatoum 				dev_err_probe(&genpd->dev, ret,
23949a6582b8SAhmad Fatoum 					      "Failed to add OPP table for index %d\n", i);
23956a0ae73dSViresh Kumar 				goto error;
23966a0ae73dSViresh Kumar 			}
23971067ae3eSViresh Kumar 
23981067ae3eSViresh Kumar 			/*
23991067ae3eSViresh Kumar 			 * Save table for faster processing while setting
24001067ae3eSViresh Kumar 			 * performance state.
24011067ae3eSViresh Kumar 			 */
2402e77dcb0bSViresh Kumar 			genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
2403dd461cd9SStephan Gerhold 			WARN_ON(IS_ERR(genpd->opp_table));
24046a0ae73dSViresh Kumar 		}
24056a0ae73dSViresh Kumar 
24066a0ae73dSViresh Kumar 		genpd->provider = &np->fwnode;
24076a0ae73dSViresh Kumar 		genpd->has_provider = true;
24080159ec67SJon Hunter 	}
24090159ec67SJon Hunter 
241040845524SThierry Reding 	ret = genpd_add_provider(np, data->xlate, data);
2411de0aa06dSJon Hunter 	if (ret < 0)
2412de0aa06dSJon Hunter 		goto error;
2413de0aa06dSJon Hunter 
2414de0aa06dSJon Hunter 	return 0;
2415de0aa06dSJon Hunter 
2416de0aa06dSJon Hunter error:
2417de0aa06dSJon Hunter 	while (i--) {
24186a0ae73dSViresh Kumar 		genpd = data->domains[i];
24196a0ae73dSViresh Kumar 
24206a0ae73dSViresh Kumar 		if (!genpd)
2421609bed67STomeu Vizoso 			continue;
24226a0ae73dSViresh Kumar 
24236a0ae73dSViresh Kumar 		genpd->provider = NULL;
24246a0ae73dSViresh Kumar 		genpd->has_provider = false;
24256a0ae73dSViresh Kumar 
24261067ae3eSViresh Kumar 		if (genpd->set_performance_state) {
24271067ae3eSViresh Kumar 			dev_pm_opp_put_opp_table(genpd->opp_table);
24286a0ae73dSViresh Kumar 			dev_pm_opp_of_remove_table(&genpd->dev);
2429de0aa06dSJon Hunter 		}
24301067ae3eSViresh Kumar 	}
24310159ec67SJon Hunter 
24320159ec67SJon Hunter 	return ret;
2433892ebdccSJon Hunter }
2434892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell);
2435aa42240aSTomasz Figa 
2436aa42240aSTomasz Figa /**
2437aa42240aSTomasz Figa  * of_genpd_del_provider() - Remove a previously registered PM domain provider
2438aa42240aSTomasz Figa  * @np: Device node pointer associated with the PM domain provider
2439aa42240aSTomasz Figa  */
of_genpd_del_provider(struct device_node * np)2440aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np)
2441aa42240aSTomasz Figa {
2442b556b15dSKrzysztof Kozlowski 	struct of_genpd_provider *cp, *tmp;
2443de0aa06dSJon Hunter 	struct generic_pm_domain *gpd;
2444aa42240aSTomasz Figa 
2445de0aa06dSJon Hunter 	mutex_lock(&gpd_list_lock);
2446aa42240aSTomasz Figa 	mutex_lock(&of_genpd_mutex);
2447b556b15dSKrzysztof Kozlowski 	list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) {
2448aa42240aSTomasz Figa 		if (cp->node == np) {
2449de0aa06dSJon Hunter 			/*
2450de0aa06dSJon Hunter 			 * For each PM domain associated with the
2451de0aa06dSJon Hunter 			 * provider, set the 'has_provider' to false
2452de0aa06dSJon Hunter 			 * so that the PM domain can be safely removed.
2453de0aa06dSJon Hunter 			 */
24546a0ae73dSViresh Kumar 			list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
24556a0ae73dSViresh Kumar 				if (gpd->provider == &np->fwnode) {
2456de0aa06dSJon Hunter 					gpd->has_provider = false;
2457de0aa06dSJon Hunter 
24586a0ae73dSViresh Kumar 					if (!gpd->set_performance_state)
24596a0ae73dSViresh Kumar 						continue;
24606a0ae73dSViresh Kumar 
24611067ae3eSViresh Kumar 					dev_pm_opp_put_opp_table(gpd->opp_table);
24626a0ae73dSViresh Kumar 					dev_pm_opp_of_remove_table(&gpd->dev);
24636a0ae73dSViresh Kumar 				}
24646a0ae73dSViresh Kumar 			}
24656a0ae73dSViresh Kumar 
2466bab2d712SSaravana Kannan 			fwnode_dev_initialized(&cp->node->fwnode, false);
2467aa42240aSTomasz Figa 			list_del(&cp->link);
2468aa42240aSTomasz Figa 			of_node_put(cp->node);
2469aa42240aSTomasz Figa 			kfree(cp);
2470aa42240aSTomasz Figa 			break;
2471aa42240aSTomasz Figa 		}
2472aa42240aSTomasz Figa 	}
2473aa42240aSTomasz Figa 	mutex_unlock(&of_genpd_mutex);
2474de0aa06dSJon Hunter 	mutex_unlock(&gpd_list_lock);
2475aa42240aSTomasz Figa }
2476aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider);
2477aa42240aSTomasz Figa 
2478aa42240aSTomasz Figa /**
2479f58d4e5aSJon Hunter  * genpd_get_from_provider() - Look-up PM domain
2480aa42240aSTomasz Figa  * @genpdspec: OF phandle args to use for look-up
2481aa42240aSTomasz Figa  *
2482aa42240aSTomasz Figa  * Looks for a PM domain provider under the node specified by @genpdspec and if
2483aa42240aSTomasz Figa  * found, uses xlate function of the provider to map phandle args to a PM
2484aa42240aSTomasz Figa  * domain.
2485aa42240aSTomasz Figa  *
2486aa42240aSTomasz Figa  * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
2487aa42240aSTomasz Figa  * on failure.
2488aa42240aSTomasz Figa  */
genpd_get_from_provider(struct of_phandle_args * genpdspec)2489f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider(
2490aa42240aSTomasz Figa 					struct of_phandle_args *genpdspec)
2491aa42240aSTomasz Figa {
2492aa42240aSTomasz Figa 	struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
2493aa42240aSTomasz Figa 	struct of_genpd_provider *provider;
2494aa42240aSTomasz Figa 
249541795a8aSJon Hunter 	if (!genpdspec)
249641795a8aSJon Hunter 		return ERR_PTR(-EINVAL);
249741795a8aSJon Hunter 
2498aa42240aSTomasz Figa 	mutex_lock(&of_genpd_mutex);
2499aa42240aSTomasz Figa 
2500aa42240aSTomasz Figa 	/* Check if we have such a provider in our array */
2501aa42240aSTomasz Figa 	list_for_each_entry(provider, &of_genpd_providers, link) {
2502aa42240aSTomasz Figa 		if (provider->node == genpdspec->np)
2503aa42240aSTomasz Figa 			genpd = provider->xlate(genpdspec, provider->data);
2504aa42240aSTomasz Figa 		if (!IS_ERR(genpd))
2505aa42240aSTomasz Figa 			break;
2506aa42240aSTomasz Figa 	}
2507aa42240aSTomasz Figa 
2508aa42240aSTomasz Figa 	mutex_unlock(&of_genpd_mutex);
2509aa42240aSTomasz Figa 
2510aa42240aSTomasz Figa 	return genpd;
2511aa42240aSTomasz Figa }
2512aa42240aSTomasz Figa 
2513aa42240aSTomasz Figa /**
2514ec69572bSJon Hunter  * of_genpd_add_device() - Add a device to an I/O PM domain
2515ec69572bSJon Hunter  * @genpdspec: OF phandle args to use for look-up PM domain
2516ec69572bSJon Hunter  * @dev: Device to be added.
2517ec69572bSJon Hunter  *
2518ec69572bSJon Hunter  * Looks-up an I/O PM domain based upon phandle args provided and adds
2519ec69572bSJon Hunter  * the device to the PM domain. Returns a negative error code on failure.
2520ec69572bSJon Hunter  */
of_genpd_add_device(struct of_phandle_args * genpdspec,struct device * dev)2521ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
2522ec69572bSJon Hunter {
2523ec69572bSJon Hunter 	struct generic_pm_domain *genpd;
252419efa5ffSJon Hunter 	int ret;
252519efa5ffSJon Hunter 
25264384a70cSUlf Hansson 	if (!dev)
25274384a70cSUlf Hansson 		return -EINVAL;
25284384a70cSUlf Hansson 
252919efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
2530ec69572bSJon Hunter 
2531f58d4e5aSJon Hunter 	genpd = genpd_get_from_provider(genpdspec);
253219efa5ffSJon Hunter 	if (IS_ERR(genpd)) {
253319efa5ffSJon Hunter 		ret = PTR_ERR(genpd);
253419efa5ffSJon Hunter 		goto out;
253519efa5ffSJon Hunter 	}
2536ec69572bSJon Hunter 
2537f9ccd7c3SUlf Hansson 	ret = genpd_add_device(genpd, dev, dev);
253819efa5ffSJon Hunter 
253919efa5ffSJon Hunter out:
254019efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
254119efa5ffSJon Hunter 
254219efa5ffSJon Hunter 	return ret;
2543ec69572bSJon Hunter }
2544ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device);
2545ec69572bSJon Hunter 
2546ec69572bSJon Hunter /**
2547ec69572bSJon Hunter  * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
2548ec69572bSJon Hunter  * @parent_spec: OF phandle args to use for parent PM domain look-up
2549ec69572bSJon Hunter  * @subdomain_spec: OF phandle args to use for subdomain look-up
2550ec69572bSJon Hunter  *
2551ec69572bSJon Hunter  * Looks-up a parent PM domain and subdomain based upon phandle args
2552ec69572bSJon Hunter  * provided and adds the subdomain to the parent PM domain. Returns a
2553ec69572bSJon Hunter  * negative error code on failure.
2554ec69572bSJon Hunter  */
of_genpd_add_subdomain(struct of_phandle_args * parent_spec,struct of_phandle_args * subdomain_spec)2555ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
2556ec69572bSJon Hunter 			   struct of_phandle_args *subdomain_spec)
2557ec69572bSJon Hunter {
2558ec69572bSJon Hunter 	struct generic_pm_domain *parent, *subdomain;
255919efa5ffSJon Hunter 	int ret;
256019efa5ffSJon Hunter 
256119efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
2562ec69572bSJon Hunter 
2563f58d4e5aSJon Hunter 	parent = genpd_get_from_provider(parent_spec);
256419efa5ffSJon Hunter 	if (IS_ERR(parent)) {
256519efa5ffSJon Hunter 		ret = PTR_ERR(parent);
256619efa5ffSJon Hunter 		goto out;
256719efa5ffSJon Hunter 	}
2568ec69572bSJon Hunter 
2569f58d4e5aSJon Hunter 	subdomain = genpd_get_from_provider(subdomain_spec);
257019efa5ffSJon Hunter 	if (IS_ERR(subdomain)) {
257119efa5ffSJon Hunter 		ret = PTR_ERR(subdomain);
257219efa5ffSJon Hunter 		goto out;
257319efa5ffSJon Hunter 	}
2574ec69572bSJon Hunter 
257519efa5ffSJon Hunter 	ret = genpd_add_subdomain(parent, subdomain);
257619efa5ffSJon Hunter 
257719efa5ffSJon Hunter out:
257819efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
257919efa5ffSJon Hunter 
258018027d6fSDmitry Osipenko 	return ret == -ENOENT ? -EPROBE_DEFER : ret;
2581ec69572bSJon Hunter }
2582ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
2583ec69572bSJon Hunter 
2584ec69572bSJon Hunter /**
2585dedd1492SUlf Hansson  * of_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
2586dedd1492SUlf Hansson  * @parent_spec: OF phandle args to use for parent PM domain look-up
2587dedd1492SUlf Hansson  * @subdomain_spec: OF phandle args to use for subdomain look-up
2588dedd1492SUlf Hansson  *
2589dedd1492SUlf Hansson  * Looks-up a parent PM domain and subdomain based upon phandle args
2590dedd1492SUlf Hansson  * provided and removes the subdomain from the parent PM domain. Returns a
2591dedd1492SUlf Hansson  * negative error code on failure.
2592dedd1492SUlf Hansson  */
of_genpd_remove_subdomain(struct of_phandle_args * parent_spec,struct of_phandle_args * subdomain_spec)2593dedd1492SUlf Hansson int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec,
2594dedd1492SUlf Hansson 			      struct of_phandle_args *subdomain_spec)
2595dedd1492SUlf Hansson {
2596dedd1492SUlf Hansson 	struct generic_pm_domain *parent, *subdomain;
2597dedd1492SUlf Hansson 	int ret;
2598dedd1492SUlf Hansson 
2599dedd1492SUlf Hansson 	mutex_lock(&gpd_list_lock);
2600dedd1492SUlf Hansson 
2601dedd1492SUlf Hansson 	parent = genpd_get_from_provider(parent_spec);
2602dedd1492SUlf Hansson 	if (IS_ERR(parent)) {
2603dedd1492SUlf Hansson 		ret = PTR_ERR(parent);
2604dedd1492SUlf Hansson 		goto out;
2605dedd1492SUlf Hansson 	}
2606dedd1492SUlf Hansson 
2607dedd1492SUlf Hansson 	subdomain = genpd_get_from_provider(subdomain_spec);
2608dedd1492SUlf Hansson 	if (IS_ERR(subdomain)) {
2609dedd1492SUlf Hansson 		ret = PTR_ERR(subdomain);
2610dedd1492SUlf Hansson 		goto out;
2611dedd1492SUlf Hansson 	}
2612dedd1492SUlf Hansson 
2613dedd1492SUlf Hansson 	ret = pm_genpd_remove_subdomain(parent, subdomain);
2614dedd1492SUlf Hansson 
2615dedd1492SUlf Hansson out:
2616dedd1492SUlf Hansson 	mutex_unlock(&gpd_list_lock);
2617dedd1492SUlf Hansson 
2618dedd1492SUlf Hansson 	return ret;
2619dedd1492SUlf Hansson }
2620dedd1492SUlf Hansson EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain);
2621dedd1492SUlf Hansson 
2622dedd1492SUlf Hansson /**
262317926551SJon Hunter  * of_genpd_remove_last - Remove the last PM domain registered for a provider
2624763663c9SYang Yingliang  * @np: Pointer to device node associated with provider
262517926551SJon Hunter  *
262617926551SJon Hunter  * Find the last PM domain that was added by a particular provider and
262717926551SJon Hunter  * remove this PM domain from the list of PM domains. The provider is
262817926551SJon Hunter  * identified by the 'provider' device structure that is passed. The PM
262917926551SJon Hunter  * domain will only be removed, if the provider associated with domain
263017926551SJon Hunter  * has been removed.
263117926551SJon Hunter  *
263217926551SJon Hunter  * Returns a valid pointer to struct generic_pm_domain on success or
263317926551SJon Hunter  * ERR_PTR() on failure.
263417926551SJon Hunter  */
of_genpd_remove_last(struct device_node * np)263517926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
263617926551SJon Hunter {
2637a7e2d1bcSKrzysztof Kozlowski 	struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT);
263817926551SJon Hunter 	int ret;
263917926551SJon Hunter 
264017926551SJon Hunter 	if (IS_ERR_OR_NULL(np))
264117926551SJon Hunter 		return ERR_PTR(-EINVAL);
264217926551SJon Hunter 
264317926551SJon Hunter 	mutex_lock(&gpd_list_lock);
2644a7e2d1bcSKrzysztof Kozlowski 	list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) {
264517926551SJon Hunter 		if (gpd->provider == &np->fwnode) {
264617926551SJon Hunter 			ret = genpd_remove(gpd);
264717926551SJon Hunter 			genpd = ret ? ERR_PTR(ret) : gpd;
264817926551SJon Hunter 			break;
264917926551SJon Hunter 		}
265017926551SJon Hunter 	}
265117926551SJon Hunter 	mutex_unlock(&gpd_list_lock);
265217926551SJon Hunter 
265317926551SJon Hunter 	return genpd;
265417926551SJon Hunter }
265517926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last);
265617926551SJon Hunter 
genpd_release_dev(struct device * dev)26573c095f32SUlf Hansson static void genpd_release_dev(struct device *dev)
26583c095f32SUlf Hansson {
2659e8b04de9SUlf Hansson 	of_node_put(dev->of_node);
26603c095f32SUlf Hansson 	kfree(dev);
26613c095f32SUlf Hansson }
26623c095f32SUlf Hansson 
26633c095f32SUlf Hansson static struct bus_type genpd_bus_type = {
26643c095f32SUlf Hansson 	.name		= "genpd",
26653c095f32SUlf Hansson };
26663c095f32SUlf Hansson 
266717926551SJon Hunter /**
2668aa42240aSTomasz Figa  * genpd_dev_pm_detach - Detach a device from its PM domain.
26698bb6944eSJon Hunter  * @dev: Device to detach.
2670aa42240aSTomasz Figa  * @power_off: Currently not used
2671aa42240aSTomasz Figa  *
2672aa42240aSTomasz Figa  * Try to locate a corresponding generic PM domain, which the device was
2673aa42240aSTomasz Figa  * attached to previously. If such is found, the device is detached from it.
2674aa42240aSTomasz Figa  */
genpd_dev_pm_detach(struct device * dev,bool power_off)2675aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off)
2676aa42240aSTomasz Figa {
2677446d999cSRussell King 	struct generic_pm_domain *pd;
267893af5e93SGeert Uytterhoeven 	unsigned int i;
2679aa42240aSTomasz Figa 	int ret = 0;
2680aa42240aSTomasz Figa 
268185168d56SUlf Hansson 	pd = dev_to_genpd(dev);
268285168d56SUlf Hansson 	if (IS_ERR(pd))
2683aa42240aSTomasz Figa 		return;
2684aa42240aSTomasz Figa 
2685aa42240aSTomasz Figa 	dev_dbg(dev, "removing from PM domain %s\n", pd->name);
2686aa42240aSTomasz Figa 
2687c016baf7SRajendra Nayak 	/* Drop the default performance state */
2688c016baf7SRajendra Nayak 	if (dev_gpd_data(dev)->default_pstate) {
2689c016baf7SRajendra Nayak 		dev_pm_genpd_set_performance_state(dev, 0);
2690c016baf7SRajendra Nayak 		dev_gpd_data(dev)->default_pstate = 0;
2691c016baf7SRajendra Nayak 	}
2692c016baf7SRajendra Nayak 
269393af5e93SGeert Uytterhoeven 	for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
269485168d56SUlf Hansson 		ret = genpd_remove_device(pd, dev);
2695aa42240aSTomasz Figa 		if (ret != -EAGAIN)
2696aa42240aSTomasz Figa 			break;
269793af5e93SGeert Uytterhoeven 
269893af5e93SGeert Uytterhoeven 		mdelay(i);
2699aa42240aSTomasz Figa 		cond_resched();
2700aa42240aSTomasz Figa 	}
2701aa42240aSTomasz Figa 
2702aa42240aSTomasz Figa 	if (ret < 0) {
2703aa42240aSTomasz Figa 		dev_err(dev, "failed to remove from PM domain %s: %d",
2704aa42240aSTomasz Figa 			pd->name, ret);
2705aa42240aSTomasz Figa 		return;
2706aa42240aSTomasz Figa 	}
2707aa42240aSTomasz Figa 
2708aa42240aSTomasz Figa 	/* Check if PM domain can be powered off after removing this device. */
2709aa42240aSTomasz Figa 	genpd_queue_power_off_work(pd);
27103c095f32SUlf Hansson 
27113c095f32SUlf Hansson 	/* Unregister the device if it was created by genpd. */
27123c095f32SUlf Hansson 	if (dev->bus == &genpd_bus_type)
27133c095f32SUlf Hansson 		device_unregister(dev);
2714aa42240aSTomasz Figa }
2715aa42240aSTomasz Figa 
genpd_dev_pm_sync(struct device * dev)2716632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev)
2717632f7ce3SRussell King {
2718632f7ce3SRussell King 	struct generic_pm_domain *pd;
2719632f7ce3SRussell King 
2720632f7ce3SRussell King 	pd = dev_to_genpd(dev);
2721632f7ce3SRussell King 	if (IS_ERR(pd))
2722632f7ce3SRussell King 		return;
2723632f7ce3SRussell King 
2724632f7ce3SRussell King 	genpd_queue_power_off_work(pd);
2725632f7ce3SRussell King }
2726632f7ce3SRussell King 
__genpd_dev_pm_attach(struct device * dev,struct device * base_dev,unsigned int index,bool power_on)272751dcf748SUlf Hansson static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
272851dcf748SUlf Hansson 				 unsigned int index, bool power_on)
2729aa42240aSTomasz Figa {
2730aa42240aSTomasz Figa 	struct of_phandle_args pd_args;
2731aa42240aSTomasz Figa 	struct generic_pm_domain *pd;
2732c016baf7SRajendra Nayak 	int pstate;
2733aa42240aSTomasz Figa 	int ret;
2734aa42240aSTomasz Figa 
2735e8b04de9SUlf Hansson 	ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
27368cb1cbd6SUlf Hansson 				"#power-domain-cells", index, &pd_args);
2737001d50c9SGeert Uytterhoeven 	if (ret < 0)
2738bcd931f2SUlf Hansson 		return ret;
2739aa42240aSTomasz Figa 
274019efa5ffSJon Hunter 	mutex_lock(&gpd_list_lock);
2741f58d4e5aSJon Hunter 	pd = genpd_get_from_provider(&pd_args);
2742265e2cf6SEric Anholt 	of_node_put(pd_args.np);
2743aa42240aSTomasz Figa 	if (IS_ERR(pd)) {
274419efa5ffSJon Hunter 		mutex_unlock(&gpd_list_lock);
2745aa42240aSTomasz Figa 		dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
2746aa42240aSTomasz Figa 			__func__, PTR_ERR(pd));
2747e20813dcSSaravana Kannan 		return driver_deferred_probe_check_state(base_dev);
2748aa42240aSTomasz Figa 	}
2749aa42240aSTomasz Figa 
2750aa42240aSTomasz Figa 	dev_dbg(dev, "adding to PM domain %s\n", pd->name);
2751aa42240aSTomasz Figa 
2752f9ccd7c3SUlf Hansson 	ret = genpd_add_device(pd, dev, base_dev);
275319efa5ffSJon Hunter 	mutex_unlock(&gpd_list_lock);
2754aa42240aSTomasz Figa 
27559a6582b8SAhmad Fatoum 	if (ret < 0)
27569a6582b8SAhmad Fatoum 		return dev_err_probe(dev, ret, "failed to add to PM domain %s\n", pd->name);
2757aa42240aSTomasz Figa 
2758aa42240aSTomasz Figa 	dev->pm_domain->detach = genpd_dev_pm_detach;
2759632f7ce3SRussell King 	dev->pm_domain->sync = genpd_dev_pm_sync;
2760aa42240aSTomasz Figa 
2761c016baf7SRajendra Nayak 	/* Set the default performance state */
2762c016baf7SRajendra Nayak 	pstate = of_get_required_opp_performance_state(dev->of_node, index);
276365616418SGeert Uytterhoeven 	if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) {
2764c016baf7SRajendra Nayak 		ret = pstate;
2765c016baf7SRajendra Nayak 		goto err;
2766c016baf7SRajendra Nayak 	} else if (pstate > 0) {
2767c016baf7SRajendra Nayak 		ret = dev_pm_genpd_set_performance_state(dev, pstate);
2768c016baf7SRajendra Nayak 		if (ret)
2769c016baf7SRajendra Nayak 			goto err;
2770c016baf7SRajendra Nayak 		dev_gpd_data(dev)->default_pstate = pstate;
2771c016baf7SRajendra Nayak 	}
2772ae8ac196SAbel Vesa 
2773ae8ac196SAbel Vesa 	if (power_on) {
2774ae8ac196SAbel Vesa 		genpd_lock(pd);
2775ae8ac196SAbel Vesa 		ret = genpd_power_on(pd, 0);
2776ae8ac196SAbel Vesa 		genpd_unlock(pd);
2777ae8ac196SAbel Vesa 	}
2778ae8ac196SAbel Vesa 
2779ae8ac196SAbel Vesa 	if (ret) {
2780ae8ac196SAbel Vesa 		/* Drop the default performance state */
2781ae8ac196SAbel Vesa 		if (dev_gpd_data(dev)->default_pstate) {
2782ae8ac196SAbel Vesa 			dev_pm_genpd_set_performance_state(dev, 0);
2783ae8ac196SAbel Vesa 			dev_gpd_data(dev)->default_pstate = 0;
2784ae8ac196SAbel Vesa 		}
2785ae8ac196SAbel Vesa 
2786ae8ac196SAbel Vesa 		genpd_remove_device(pd, dev);
2787ae8ac196SAbel Vesa 		return -EPROBE_DEFER;
2788ae8ac196SAbel Vesa 	}
2789ae8ac196SAbel Vesa 
2790c016baf7SRajendra Nayak 	return 1;
2791c016baf7SRajendra Nayak 
2792c016baf7SRajendra Nayak err:
2793c016baf7SRajendra Nayak 	dev_err(dev, "failed to set required performance state for power-domain %s: %d\n",
2794c016baf7SRajendra Nayak 		pd->name, ret);
2795c016baf7SRajendra Nayak 	genpd_remove_device(pd, dev);
2796c016baf7SRajendra Nayak 	return ret;
2797aa42240aSTomasz Figa }
27988cb1cbd6SUlf Hansson 
27998cb1cbd6SUlf Hansson /**
28008cb1cbd6SUlf Hansson  * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
28018cb1cbd6SUlf Hansson  * @dev: Device to attach.
28028cb1cbd6SUlf Hansson  *
28038cb1cbd6SUlf Hansson  * Parse device's OF node to find a PM domain specifier. If such is found,
28048cb1cbd6SUlf Hansson  * attaches the device to retrieved pm_domain ops.
28058cb1cbd6SUlf Hansson  *
28068cb1cbd6SUlf Hansson  * Returns 1 on successfully attached PM domain, 0 when the device don't need a
28078cb1cbd6SUlf Hansson  * PM domain or when multiple power-domains exists for it, else a negative error
28088cb1cbd6SUlf Hansson  * code. Note that if a power-domain exists for the device, but it cannot be
28098cb1cbd6SUlf Hansson  * found or turned on, then return -EPROBE_DEFER to ensure that the device is
28108cb1cbd6SUlf Hansson  * not probed and to re-try again later.
28118cb1cbd6SUlf Hansson  */
genpd_dev_pm_attach(struct device * dev)28128cb1cbd6SUlf Hansson int genpd_dev_pm_attach(struct device *dev)
28138cb1cbd6SUlf Hansson {
28148cb1cbd6SUlf Hansson 	if (!dev->of_node)
28158cb1cbd6SUlf Hansson 		return 0;
28168cb1cbd6SUlf Hansson 
28178cb1cbd6SUlf Hansson 	/*
28188cb1cbd6SUlf Hansson 	 * Devices with multiple PM domains must be attached separately, as we
28198cb1cbd6SUlf Hansson 	 * can only attach one PM domain per device.
28208cb1cbd6SUlf Hansson 	 */
28218cb1cbd6SUlf Hansson 	if (of_count_phandle_with_args(dev->of_node, "power-domains",
28228cb1cbd6SUlf Hansson 				       "#power-domain-cells") != 1)
28238cb1cbd6SUlf Hansson 		return 0;
28248cb1cbd6SUlf Hansson 
282551dcf748SUlf Hansson 	return __genpd_dev_pm_attach(dev, dev, 0, true);
28268cb1cbd6SUlf Hansson }
2827aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
282830f60428SLina Iyer 
28293c095f32SUlf Hansson /**
28303c095f32SUlf Hansson  * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains.
28313c095f32SUlf Hansson  * @dev: The device used to lookup the PM domain.
28323c095f32SUlf Hansson  * @index: The index of the PM domain.
28333c095f32SUlf Hansson  *
28343c095f32SUlf Hansson  * Parse device's OF node to find a PM domain specifier at the provided @index.
28353c095f32SUlf Hansson  * If such is found, creates a virtual device and attaches it to the retrieved
28363c095f32SUlf Hansson  * pm_domain ops. To deal with detaching of the virtual device, the ->detach()
28373c095f32SUlf Hansson  * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach().
28383c095f32SUlf Hansson  *
28393c095f32SUlf Hansson  * Returns the created virtual device if successfully attached PM domain, NULL
28403c095f32SUlf Hansson  * when the device don't need a PM domain, else an ERR_PTR() in case of
28413c095f32SUlf Hansson  * failures. If a power-domain exists for the device, but cannot be found or
28423c095f32SUlf Hansson  * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device
28433c095f32SUlf Hansson  * is not probed and to re-try again later.
28443c095f32SUlf Hansson  */
genpd_dev_pm_attach_by_id(struct device * dev,unsigned int index)28453c095f32SUlf Hansson struct device *genpd_dev_pm_attach_by_id(struct device *dev,
28463c095f32SUlf Hansson 					 unsigned int index)
28473c095f32SUlf Hansson {
2848560928b2SViresh Kumar 	struct device *virt_dev;
28493c095f32SUlf Hansson 	int num_domains;
28503c095f32SUlf Hansson 	int ret;
28513c095f32SUlf Hansson 
28523c095f32SUlf Hansson 	if (!dev->of_node)
28533c095f32SUlf Hansson 		return NULL;
28543c095f32SUlf Hansson 
28553ccf3f0cSUlf Hansson 	/* Verify that the index is within a valid range. */
28563c095f32SUlf Hansson 	num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
28573c095f32SUlf Hansson 						 "#power-domain-cells");
28583ccf3f0cSUlf Hansson 	if (index >= num_domains)
28593c095f32SUlf Hansson 		return NULL;
28603c095f32SUlf Hansson 
28613c095f32SUlf Hansson 	/* Allocate and register device on the genpd bus. */
2862560928b2SViresh Kumar 	virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
2863560928b2SViresh Kumar 	if (!virt_dev)
28643c095f32SUlf Hansson 		return ERR_PTR(-ENOMEM);
28653c095f32SUlf Hansson 
2866560928b2SViresh Kumar 	dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev));
2867560928b2SViresh Kumar 	virt_dev->bus = &genpd_bus_type;
2868560928b2SViresh Kumar 	virt_dev->release = genpd_release_dev;
2869e8b04de9SUlf Hansson 	virt_dev->of_node = of_node_get(dev->of_node);
28703c095f32SUlf Hansson 
2871560928b2SViresh Kumar 	ret = device_register(virt_dev);
28723c095f32SUlf Hansson 	if (ret) {
287371b77697SUlf Hansson 		put_device(virt_dev);
28743c095f32SUlf Hansson 		return ERR_PTR(ret);
28753c095f32SUlf Hansson 	}
28763c095f32SUlf Hansson 
28773c095f32SUlf Hansson 	/* Try to attach the device to the PM domain at the specified index. */
287851dcf748SUlf Hansson 	ret = __genpd_dev_pm_attach(virt_dev, dev, index, false);
28793c095f32SUlf Hansson 	if (ret < 1) {
2880560928b2SViresh Kumar 		device_unregister(virt_dev);
28813c095f32SUlf Hansson 		return ret ? ERR_PTR(ret) : NULL;
28823c095f32SUlf Hansson 	}
28833c095f32SUlf Hansson 
2884560928b2SViresh Kumar 	pm_runtime_enable(virt_dev);
2885560928b2SViresh Kumar 	genpd_queue_power_off_work(dev_to_genpd(virt_dev));
28863c095f32SUlf Hansson 
2887560928b2SViresh Kumar 	return virt_dev;
28883c095f32SUlf Hansson }
28893c095f32SUlf Hansson EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
28903c095f32SUlf Hansson 
28915d6be70aSUlf Hansson /**
28925d6be70aSUlf Hansson  * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains.
28935d6be70aSUlf Hansson  * @dev: The device used to lookup the PM domain.
28945d6be70aSUlf Hansson  * @name: The name of the PM domain.
28955d6be70aSUlf Hansson  *
28965d6be70aSUlf Hansson  * Parse device's OF node to find a PM domain specifier using the
28975d6be70aSUlf Hansson  * power-domain-names DT property. For further description see
28985d6be70aSUlf Hansson  * genpd_dev_pm_attach_by_id().
28995d6be70aSUlf Hansson  */
genpd_dev_pm_attach_by_name(struct device * dev,const char * name)29007416f1f2SDouglas Anderson struct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name)
29015d6be70aSUlf Hansson {
29025d6be70aSUlf Hansson 	int index;
29035d6be70aSUlf Hansson 
29045d6be70aSUlf Hansson 	if (!dev->of_node)
29055d6be70aSUlf Hansson 		return NULL;
29065d6be70aSUlf Hansson 
29075d6be70aSUlf Hansson 	index = of_property_match_string(dev->of_node, "power-domain-names",
29085d6be70aSUlf Hansson 					 name);
29095d6be70aSUlf Hansson 	if (index < 0)
29105d6be70aSUlf Hansson 		return NULL;
29115d6be70aSUlf Hansson 
29125d6be70aSUlf Hansson 	return genpd_dev_pm_attach_by_id(dev, index);
29135d6be70aSUlf Hansson }
29145d6be70aSUlf Hansson 
291530f60428SLina Iyer static const struct of_device_id idle_state_match[] = {
2916598da548SLina Iyer 	{ .compatible = "domain-idle-state", },
291730f60428SLina Iyer 	{ }
291830f60428SLina Iyer };
291930f60428SLina Iyer 
genpd_parse_state(struct genpd_power_state * genpd_state,struct device_node * state_node)292030f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state,
292130f60428SLina Iyer 				    struct device_node *state_node)
292230f60428SLina Iyer {
292330f60428SLina Iyer 	int err;
292430f60428SLina Iyer 	u32 residency;
292530f60428SLina Iyer 	u32 entry_latency, exit_latency;
292630f60428SLina Iyer 
292730f60428SLina Iyer 	err = of_property_read_u32(state_node, "entry-latency-us",
292830f60428SLina Iyer 						&entry_latency);
292930f60428SLina Iyer 	if (err) {
2930ea11e94bSRob Herring 		pr_debug(" * %pOF missing entry-latency-us property\n",
2931ea11e94bSRob Herring 			 state_node);
293230f60428SLina Iyer 		return -EINVAL;
293330f60428SLina Iyer 	}
293430f60428SLina Iyer 
293530f60428SLina Iyer 	err = of_property_read_u32(state_node, "exit-latency-us",
293630f60428SLina Iyer 						&exit_latency);
293730f60428SLina Iyer 	if (err) {
2938ea11e94bSRob Herring 		pr_debug(" * %pOF missing exit-latency-us property\n",
2939ea11e94bSRob Herring 			 state_node);
294030f60428SLina Iyer 		return -EINVAL;
294130f60428SLina Iyer 	}
294230f60428SLina Iyer 
294330f60428SLina Iyer 	err = of_property_read_u32(state_node, "min-residency-us", &residency);
294430f60428SLina Iyer 	if (!err)
2945e5d1c872SNikita Zhandarovich 		genpd_state->residency_ns = 1000LL * residency;
294630f60428SLina Iyer 
2947e5d1c872SNikita Zhandarovich 	genpd_state->power_on_latency_ns = 1000LL * exit_latency;
2948e5d1c872SNikita Zhandarovich 	genpd_state->power_off_latency_ns = 1000LL * entry_latency;
29490c9b694aSLina Iyer 	genpd_state->fwnode = &state_node->fwnode;
295030f60428SLina Iyer 
295130f60428SLina Iyer 	return 0;
295230f60428SLina Iyer }
295330f60428SLina Iyer 
genpd_iterate_idle_states(struct device_node * dn,struct genpd_power_state * states)2954a3381e3aSUlf Hansson static int genpd_iterate_idle_states(struct device_node *dn,
2955a3381e3aSUlf Hansson 				     struct genpd_power_state *states)
2956a3381e3aSUlf Hansson {
2957a3381e3aSUlf Hansson 	int ret;
2958a3381e3aSUlf Hansson 	struct of_phandle_iterator it;
2959a3381e3aSUlf Hansson 	struct device_node *np;
2960a3381e3aSUlf Hansson 	int i = 0;
2961a3381e3aSUlf Hansson 
2962a3381e3aSUlf Hansson 	ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL);
2963a3381e3aSUlf Hansson 	if (ret <= 0)
296456cb2689SUlf Hansson 		return ret == -ENOENT ? 0 : ret;
2965a3381e3aSUlf Hansson 
2966a3381e3aSUlf Hansson 	/* Loop over the phandles until all the requested entry is found */
2967a3381e3aSUlf Hansson 	of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) {
2968a3381e3aSUlf Hansson 		np = it.node;
2969a3381e3aSUlf Hansson 		if (!of_match_node(idle_state_match, np))
2970a3381e3aSUlf Hansson 			continue;
2971e0c57a5cSSudeep Holla 
2972e0c57a5cSSudeep Holla 		if (!of_device_is_available(np))
2973e0c57a5cSSudeep Holla 			continue;
2974e0c57a5cSSudeep Holla 
2975a3381e3aSUlf Hansson 		if (states) {
2976a3381e3aSUlf Hansson 			ret = genpd_parse_state(&states[i], np);
2977a3381e3aSUlf Hansson 			if (ret) {
2978a3381e3aSUlf Hansson 				pr_err("Parsing idle state node %pOF failed with err %d\n",
2979a3381e3aSUlf Hansson 				       np, ret);
2980a3381e3aSUlf Hansson 				of_node_put(np);
2981a3381e3aSUlf Hansson 				return ret;
2982a3381e3aSUlf Hansson 			}
2983a3381e3aSUlf Hansson 		}
2984a3381e3aSUlf Hansson 		i++;
2985a3381e3aSUlf Hansson 	}
2986a3381e3aSUlf Hansson 
2987a3381e3aSUlf Hansson 	return i;
2988a3381e3aSUlf Hansson }
2989a3381e3aSUlf Hansson 
299030f60428SLina Iyer /**
299130f60428SLina Iyer  * of_genpd_parse_idle_states: Return array of idle states for the genpd.
299230f60428SLina Iyer  *
299330f60428SLina Iyer  * @dn: The genpd device node
299430f60428SLina Iyer  * @states: The pointer to which the state array will be saved.
299530f60428SLina Iyer  * @n: The count of elements in the array returned from this function.
299630f60428SLina Iyer  *
299730f60428SLina Iyer  * Returns the device states parsed from the OF node. The memory for the states
299830f60428SLina Iyer  * is allocated by this function and is the responsibility of the caller to
29992c361684SUlf Hansson  * free the memory after use. If any or zero compatible domain idle states is
30002c361684SUlf Hansson  * found it returns 0 and in case of errors, a negative error code is returned.
300130f60428SLina Iyer  */
of_genpd_parse_idle_states(struct device_node * dn,struct genpd_power_state ** states,int * n)300230f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn,
300330f60428SLina Iyer 			struct genpd_power_state **states, int *n)
300430f60428SLina Iyer {
300530f60428SLina Iyer 	struct genpd_power_state *st;
3006a3381e3aSUlf Hansson 	int ret;
300730f60428SLina Iyer 
3008a3381e3aSUlf Hansson 	ret = genpd_iterate_idle_states(dn, NULL);
30092c361684SUlf Hansson 	if (ret < 0)
30102c361684SUlf Hansson 		return ret;
30112c361684SUlf Hansson 
30122c361684SUlf Hansson 	if (!ret) {
30132c361684SUlf Hansson 		*states = NULL;
30142c361684SUlf Hansson 		*n = 0;
30152c361684SUlf Hansson 		return 0;
30162c361684SUlf Hansson 	}
301730f60428SLina Iyer 
3018a3381e3aSUlf Hansson 	st = kcalloc(ret, sizeof(*st), GFP_KERNEL);
301930f60428SLina Iyer 	if (!st)
302030f60428SLina Iyer 		return -ENOMEM;
302130f60428SLina Iyer 
3022a3381e3aSUlf Hansson 	ret = genpd_iterate_idle_states(dn, st);
3023a3381e3aSUlf Hansson 	if (ret <= 0) {
302430f60428SLina Iyer 		kfree(st);
3025a3381e3aSUlf Hansson 		return ret < 0 ? ret : -EINVAL;
302630f60428SLina Iyer 	}
302730f60428SLina Iyer 
302830f60428SLina Iyer 	*states = st;
3029a3381e3aSUlf Hansson 	*n = ret;
303030f60428SLina Iyer 
303130f60428SLina Iyer 	return 0;
303230f60428SLina Iyer }
303330f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
303430f60428SLina Iyer 
30356e41766aSViresh Kumar /**
3036e38f89d3SViresh Kumar  * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node.
3037e38f89d3SViresh Kumar  *
3038e38f89d3SViresh Kumar  * @genpd_dev: Genpd's device for which the performance-state needs to be found.
3039e38f89d3SViresh Kumar  * @opp: struct dev_pm_opp of the OPP for which we need to find performance
3040e38f89d3SViresh Kumar  *	state.
3041e38f89d3SViresh Kumar  *
3042e38f89d3SViresh Kumar  * Returns performance state encoded in the OPP of the genpd. This calls
3043e38f89d3SViresh Kumar  * platform specific genpd->opp_to_performance_state() callback to translate
3044e38f89d3SViresh Kumar  * power domain OPP to performance state.
3045e38f89d3SViresh Kumar  *
3046e38f89d3SViresh Kumar  * Returns performance state on success and 0 on failure.
3047e38f89d3SViresh Kumar  */
pm_genpd_opp_to_performance_state(struct device * genpd_dev,struct dev_pm_opp * opp)3048e38f89d3SViresh Kumar unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev,
3049e38f89d3SViresh Kumar 					       struct dev_pm_opp *opp)
3050e38f89d3SViresh Kumar {
3051e38f89d3SViresh Kumar 	struct generic_pm_domain *genpd = NULL;
3052e38f89d3SViresh Kumar 	int state;
3053e38f89d3SViresh Kumar 
3054e38f89d3SViresh Kumar 	genpd = container_of(genpd_dev, struct generic_pm_domain, dev);
3055e38f89d3SViresh Kumar 
3056e38f89d3SViresh Kumar 	if (unlikely(!genpd->opp_to_performance_state))
3057e38f89d3SViresh Kumar 		return 0;
3058e38f89d3SViresh Kumar 
3059e38f89d3SViresh Kumar 	genpd_lock(genpd);
3060e38f89d3SViresh Kumar 	state = genpd->opp_to_performance_state(genpd, opp);
3061e38f89d3SViresh Kumar 	genpd_unlock(genpd);
3062e38f89d3SViresh Kumar 
3063e38f89d3SViresh Kumar 	return state;
3064e38f89d3SViresh Kumar }
3065e38f89d3SViresh Kumar EXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state);
3066e38f89d3SViresh Kumar 
genpd_bus_init(void)30673c095f32SUlf Hansson static int __init genpd_bus_init(void)
30683c095f32SUlf Hansson {
30693c095f32SUlf Hansson 	return bus_register(&genpd_bus_type);
30703c095f32SUlf Hansson }
30713c095f32SUlf Hansson core_initcall(genpd_bus_init);
30723c095f32SUlf Hansson 
3073d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
30742bd5306aSMaciej Matraszek 
30752bd5306aSMaciej Matraszek 
30762bd5306aSMaciej Matraszek /***        debugfs support        ***/
30772bd5306aSMaciej Matraszek 
30788b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS
30792bd5306aSMaciej Matraszek /*
30802bd5306aSMaciej Matraszek  * TODO: This function is a slightly modified version of rtpm_status_show
3081d30d819dSRafael J. Wysocki  * from sysfs.c, so generalize it.
30822bd5306aSMaciej Matraszek  */
rtpm_status_str(struct seq_file * s,struct device * dev)30832bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev)
30842bd5306aSMaciej Matraszek {
30852bd5306aSMaciej Matraszek 	static const char * const status_lookup[] = {
30862bd5306aSMaciej Matraszek 		[RPM_ACTIVE] = "active",
30872bd5306aSMaciej Matraszek 		[RPM_RESUMING] = "resuming",
30882bd5306aSMaciej Matraszek 		[RPM_SUSPENDED] = "suspended",
30892bd5306aSMaciej Matraszek 		[RPM_SUSPENDING] = "suspending"
30902bd5306aSMaciej Matraszek 	};
30912bd5306aSMaciej Matraszek 	const char *p = "";
30922bd5306aSMaciej Matraszek 
30932bd5306aSMaciej Matraszek 	if (dev->power.runtime_error)
30942bd5306aSMaciej Matraszek 		p = "error";
30952bd5306aSMaciej Matraszek 	else if (dev->power.disable_depth)
30962bd5306aSMaciej Matraszek 		p = "unsupported";
30972bd5306aSMaciej Matraszek 	else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup))
30982bd5306aSMaciej Matraszek 		p = status_lookup[dev->power.runtime_status];
30992bd5306aSMaciej Matraszek 	else
31002bd5306aSMaciej Matraszek 		WARN_ON(1);
31012bd5306aSMaciej Matraszek 
310245fbc464SDmitry Osipenko 	seq_printf(s, "%-25s  ", p);
310345fbc464SDmitry Osipenko }
310445fbc464SDmitry Osipenko 
perf_status_str(struct seq_file * s,struct device * dev)310545fbc464SDmitry Osipenko static void perf_status_str(struct seq_file *s, struct device *dev)
310645fbc464SDmitry Osipenko {
310745fbc464SDmitry Osipenko 	struct generic_pm_domain_data *gpd_data;
310845fbc464SDmitry Osipenko 
310945fbc464SDmitry Osipenko 	gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
311045fbc464SDmitry Osipenko 	seq_put_decimal_ull(s, "", gpd_data->performance_state);
31112bd5306aSMaciej Matraszek }
31122bd5306aSMaciej Matraszek 
genpd_summary_one(struct seq_file * s,struct generic_pm_domain * genpd)31139e9704eaSUlf Hansson static int genpd_summary_one(struct seq_file *s,
311466a5ca4bSKevin Hilman 			struct generic_pm_domain *genpd)
31152bd5306aSMaciej Matraszek {
31162bd5306aSMaciej Matraszek 	static const char * const status_lookup[] = {
311749f618e1SUlf Hansson 		[GENPD_STATE_ON] = "on",
311849f618e1SUlf Hansson 		[GENPD_STATE_OFF] = "off"
31192bd5306aSMaciej Matraszek 	};
31202bd5306aSMaciej Matraszek 	struct pm_domain_data *pm_data;
31212bd5306aSMaciej Matraszek 	const char *kobj_path;
31222bd5306aSMaciej Matraszek 	struct gpd_link *link;
31236954d432SGeert Uytterhoeven 	char state[16];
31242bd5306aSMaciej Matraszek 	int ret;
31252bd5306aSMaciej Matraszek 
312635241d12SLina Iyer 	ret = genpd_lock_interruptible(genpd);
31272bd5306aSMaciej Matraszek 	if (ret)
31282bd5306aSMaciej Matraszek 		return -ERESTARTSYS;
31292bd5306aSMaciej Matraszek 
313066a5ca4bSKevin Hilman 	if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup)))
31312bd5306aSMaciej Matraszek 		goto exit;
313241e2c8e0SUlf Hansson 	if (!genpd_status_on(genpd))
31330ba554e4SGeert Uytterhoeven 		snprintf(state, sizeof(state), "%s-%u",
31346954d432SGeert Uytterhoeven 			 status_lookup[genpd->status], genpd->state_idx);
3135fc5cbf0cSAxel Haslam 	else
31366954d432SGeert Uytterhoeven 		snprintf(state, sizeof(state), "%s",
31376954d432SGeert Uytterhoeven 			 status_lookup[genpd->status]);
313845fbc464SDmitry Osipenko 	seq_printf(s, "%-30s  %-50s %u", genpd->name, state, genpd->performance_state);
31392bd5306aSMaciej Matraszek 
31402bd5306aSMaciej Matraszek 	/*
31412bd5306aSMaciej Matraszek 	 * Modifications on the list require holding locks on both
31428d87ae48SKees Cook 	 * parent and child, so we are safe.
314366a5ca4bSKevin Hilman 	 * Also genpd->name is immutable.
31442bd5306aSMaciej Matraszek 	 */
31458d87ae48SKees Cook 	list_for_each_entry(link, &genpd->parent_links, parent_node) {
314645fbc464SDmitry Osipenko 		if (list_is_first(&link->parent_node, &genpd->parent_links))
314745fbc464SDmitry Osipenko 			seq_printf(s, "\n%48s", " ");
31488d87ae48SKees Cook 		seq_printf(s, "%s", link->child->name);
31498d87ae48SKees Cook 		if (!list_is_last(&link->parent_node, &genpd->parent_links))
31502bd5306aSMaciej Matraszek 			seq_puts(s, ", ");
31512bd5306aSMaciej Matraszek 	}
31522bd5306aSMaciej Matraszek 
315366a5ca4bSKevin Hilman 	list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
3154d716f479SLina Iyer 		kobj_path = kobject_get_path(&pm_data->dev->kobj,
3155d716f479SLina Iyer 				genpd_is_irq_safe(genpd) ?
3156d716f479SLina Iyer 				GFP_ATOMIC : GFP_KERNEL);
31572bd5306aSMaciej Matraszek 		if (kobj_path == NULL)
31582bd5306aSMaciej Matraszek 			continue;
31592bd5306aSMaciej Matraszek 
31602bd5306aSMaciej Matraszek 		seq_printf(s, "\n    %-50s  ", kobj_path);
31612bd5306aSMaciej Matraszek 		rtpm_status_str(s, pm_data->dev);
316245fbc464SDmitry Osipenko 		perf_status_str(s, pm_data->dev);
31632bd5306aSMaciej Matraszek 		kfree(kobj_path);
31642bd5306aSMaciej Matraszek 	}
31652bd5306aSMaciej Matraszek 
31662bd5306aSMaciej Matraszek 	seq_puts(s, "\n");
31672bd5306aSMaciej Matraszek exit:
316835241d12SLina Iyer 	genpd_unlock(genpd);
31692bd5306aSMaciej Matraszek 
31702bd5306aSMaciej Matraszek 	return 0;
31712bd5306aSMaciej Matraszek }
31722bd5306aSMaciej Matraszek 
summary_show(struct seq_file * s,void * data)3173d32dcc6cSYangtao Li static int summary_show(struct seq_file *s, void *data)
31742bd5306aSMaciej Matraszek {
317566a5ca4bSKevin Hilman 	struct generic_pm_domain *genpd;
31762bd5306aSMaciej Matraszek 	int ret = 0;
31772bd5306aSMaciej Matraszek 
317845fbc464SDmitry Osipenko 	seq_puts(s, "domain                          status          children                           performance\n");
31792bd5306aSMaciej Matraszek 	seq_puts(s, "    /device                                             runtime status\n");
318045fbc464SDmitry Osipenko 	seq_puts(s, "----------------------------------------------------------------------------------------------\n");
31812bd5306aSMaciej Matraszek 
31822bd5306aSMaciej Matraszek 	ret = mutex_lock_interruptible(&gpd_list_lock);
31832bd5306aSMaciej Matraszek 	if (ret)
31842bd5306aSMaciej Matraszek 		return -ERESTARTSYS;
31852bd5306aSMaciej Matraszek 
318666a5ca4bSKevin Hilman 	list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
31879e9704eaSUlf Hansson 		ret = genpd_summary_one(s, genpd);
31882bd5306aSMaciej Matraszek 		if (ret)
31892bd5306aSMaciej Matraszek 			break;
31902bd5306aSMaciej Matraszek 	}
31912bd5306aSMaciej Matraszek 	mutex_unlock(&gpd_list_lock);
31922bd5306aSMaciej Matraszek 
31932bd5306aSMaciej Matraszek 	return ret;
31942bd5306aSMaciej Matraszek }
31952bd5306aSMaciej Matraszek 
status_show(struct seq_file * s,void * data)3196d32dcc6cSYangtao Li static int status_show(struct seq_file *s, void *data)
31972bd5306aSMaciej Matraszek {
3198b6a1d093SThara Gopinath 	static const char * const status_lookup[] = {
319949f618e1SUlf Hansson 		[GENPD_STATE_ON] = "on",
320049f618e1SUlf Hansson 		[GENPD_STATE_OFF] = "off"
3201b6a1d093SThara Gopinath 	};
3202b6a1d093SThara Gopinath 
3203b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3204b6a1d093SThara Gopinath 	int ret = 0;
3205b6a1d093SThara Gopinath 
3206b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3207b6a1d093SThara Gopinath 	if (ret)
3208b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3209b6a1d093SThara Gopinath 
3210b6a1d093SThara Gopinath 	if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup)))
3211b6a1d093SThara Gopinath 		goto exit;
3212b6a1d093SThara Gopinath 
321349f618e1SUlf Hansson 	if (genpd->status == GENPD_STATE_OFF)
3214b6a1d093SThara Gopinath 		seq_printf(s, "%s-%u\n", status_lookup[genpd->status],
3215b6a1d093SThara Gopinath 			genpd->state_idx);
3216b6a1d093SThara Gopinath 	else
3217b6a1d093SThara Gopinath 		seq_printf(s, "%s\n", status_lookup[genpd->status]);
3218b6a1d093SThara Gopinath exit:
3219b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3220b6a1d093SThara Gopinath 	return ret;
32212bd5306aSMaciej Matraszek }
32222bd5306aSMaciej Matraszek 
sub_domains_show(struct seq_file * s,void * data)3223d32dcc6cSYangtao Li static int sub_domains_show(struct seq_file *s, void *data)
3224b6a1d093SThara Gopinath {
3225b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3226b6a1d093SThara Gopinath 	struct gpd_link *link;
3227b6a1d093SThara Gopinath 	int ret = 0;
3228b6a1d093SThara Gopinath 
3229b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3230b6a1d093SThara Gopinath 	if (ret)
3231b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3232b6a1d093SThara Gopinath 
32338d87ae48SKees Cook 	list_for_each_entry(link, &genpd->parent_links, parent_node)
32348d87ae48SKees Cook 		seq_printf(s, "%s\n", link->child->name);
3235b6a1d093SThara Gopinath 
3236b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3237b6a1d093SThara Gopinath 	return ret;
3238b6a1d093SThara Gopinath }
3239b6a1d093SThara Gopinath 
idle_states_show(struct seq_file * s,void * data)3240d32dcc6cSYangtao Li static int idle_states_show(struct seq_file *s, void *data)
3241b6a1d093SThara Gopinath {
3242b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3243bd40cbb0SUlf Hansson 	u64 now, delta, idle_time = 0;
3244b6a1d093SThara Gopinath 	unsigned int i;
3245b6a1d093SThara Gopinath 	int ret = 0;
3246b6a1d093SThara Gopinath 
3247b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3248b6a1d093SThara Gopinath 	if (ret)
3249b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3250b6a1d093SThara Gopinath 
3251c6a113b5SLina Iyer 	seq_puts(s, "State          Time Spent(ms) Usage          Rejected\n");
3252b6a1d093SThara Gopinath 
3253b6a1d093SThara Gopinath 	for (i = 0; i < genpd->state_count; i++) {
3254bd40cbb0SUlf Hansson 		idle_time += genpd->states[i].idle_time;
3255b6a1d093SThara Gopinath 
3256bd40cbb0SUlf Hansson 		if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) {
3257bd40cbb0SUlf Hansson 			now = ktime_get_mono_fast_ns();
3258bd40cbb0SUlf Hansson 			if (now > genpd->accounting_time) {
3259bd40cbb0SUlf Hansson 				delta = now - genpd->accounting_time;
3260bd40cbb0SUlf Hansson 				idle_time += delta;
3261bd40cbb0SUlf Hansson 			}
3262bd40cbb0SUlf Hansson 		}
3263b6a1d093SThara Gopinath 
3264bd40cbb0SUlf Hansson 		do_div(idle_time, NSEC_PER_MSEC);
3265bd40cbb0SUlf Hansson 		seq_printf(s, "S%-13i %-14llu %-14llu %llu\n", i, idle_time,
3266c6a113b5SLina Iyer 			   genpd->states[i].usage, genpd->states[i].rejected);
3267b6a1d093SThara Gopinath 	}
3268b6a1d093SThara Gopinath 
3269b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3270b6a1d093SThara Gopinath 	return ret;
3271b6a1d093SThara Gopinath }
3272b6a1d093SThara Gopinath 
active_time_show(struct seq_file * s,void * data)3273d32dcc6cSYangtao Li static int active_time_show(struct seq_file *s, void *data)
3274b6a1d093SThara Gopinath {
3275b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3276bd40cbb0SUlf Hansson 	u64 now, on_time, delta = 0;
3277b6a1d093SThara Gopinath 	int ret = 0;
3278b6a1d093SThara Gopinath 
3279b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3280b6a1d093SThara Gopinath 	if (ret)
3281b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3282b6a1d093SThara Gopinath 
3283bd40cbb0SUlf Hansson 	if (genpd->status == GENPD_STATE_ON) {
3284bd40cbb0SUlf Hansson 		now = ktime_get_mono_fast_ns();
3285bd40cbb0SUlf Hansson 		if (now > genpd->accounting_time)
3286bd40cbb0SUlf Hansson 			delta = now - genpd->accounting_time;
3287bd40cbb0SUlf Hansson 	}
3288b6a1d093SThara Gopinath 
3289bd40cbb0SUlf Hansson 	on_time = genpd->on_time + delta;
3290bd40cbb0SUlf Hansson 	do_div(on_time, NSEC_PER_MSEC);
3291bd40cbb0SUlf Hansson 	seq_printf(s, "%llu ms\n", on_time);
3292b6a1d093SThara Gopinath 
3293b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3294b6a1d093SThara Gopinath 	return ret;
3295b6a1d093SThara Gopinath }
3296b6a1d093SThara Gopinath 
total_idle_time_show(struct seq_file * s,void * data)3297d32dcc6cSYangtao Li static int total_idle_time_show(struct seq_file *s, void *data)
3298b6a1d093SThara Gopinath {
3299b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3300bd40cbb0SUlf Hansson 	u64 now, delta, total = 0;
3301b6a1d093SThara Gopinath 	unsigned int i;
3302b6a1d093SThara Gopinath 	int ret = 0;
3303b6a1d093SThara Gopinath 
3304b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3305b6a1d093SThara Gopinath 	if (ret)
3306b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3307b6a1d093SThara Gopinath 
3308b6a1d093SThara Gopinath 	for (i = 0; i < genpd->state_count; i++) {
3309bd40cbb0SUlf Hansson 		total += genpd->states[i].idle_time;
3310b6a1d093SThara Gopinath 
3311bd40cbb0SUlf Hansson 		if (genpd->status == GENPD_STATE_OFF && genpd->state_idx == i) {
3312bd40cbb0SUlf Hansson 			now = ktime_get_mono_fast_ns();
3313bd40cbb0SUlf Hansson 			if (now > genpd->accounting_time) {
3314bd40cbb0SUlf Hansson 				delta = now - genpd->accounting_time;
3315bd40cbb0SUlf Hansson 				total += delta;
3316b6a1d093SThara Gopinath 			}
3317bd40cbb0SUlf Hansson 		}
3318bd40cbb0SUlf Hansson 	}
3319b6a1d093SThara Gopinath 
3320bd40cbb0SUlf Hansson 	do_div(total, NSEC_PER_MSEC);
3321bd40cbb0SUlf Hansson 	seq_printf(s, "%llu ms\n", total);
3322b6a1d093SThara Gopinath 
3323b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3324b6a1d093SThara Gopinath 	return ret;
3325b6a1d093SThara Gopinath }
3326b6a1d093SThara Gopinath 
3327b6a1d093SThara Gopinath 
devices_show(struct seq_file * s,void * data)3328d32dcc6cSYangtao Li static int devices_show(struct seq_file *s, void *data)
3329b6a1d093SThara Gopinath {
3330b6a1d093SThara Gopinath 	struct generic_pm_domain *genpd = s->private;
3331b6a1d093SThara Gopinath 	struct pm_domain_data *pm_data;
3332b6a1d093SThara Gopinath 	const char *kobj_path;
3333b6a1d093SThara Gopinath 	int ret = 0;
3334b6a1d093SThara Gopinath 
3335b6a1d093SThara Gopinath 	ret = genpd_lock_interruptible(genpd);
3336b6a1d093SThara Gopinath 	if (ret)
3337b6a1d093SThara Gopinath 		return -ERESTARTSYS;
3338b6a1d093SThara Gopinath 
3339b6a1d093SThara Gopinath 	list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
3340b6a1d093SThara Gopinath 		kobj_path = kobject_get_path(&pm_data->dev->kobj,
3341b6a1d093SThara Gopinath 				genpd_is_irq_safe(genpd) ?
3342b6a1d093SThara Gopinath 				GFP_ATOMIC : GFP_KERNEL);
3343b6a1d093SThara Gopinath 		if (kobj_path == NULL)
3344b6a1d093SThara Gopinath 			continue;
3345b6a1d093SThara Gopinath 
3346b6a1d093SThara Gopinath 		seq_printf(s, "%s\n", kobj_path);
3347b6a1d093SThara Gopinath 		kfree(kobj_path);
3348b6a1d093SThara Gopinath 	}
3349b6a1d093SThara Gopinath 
3350b6a1d093SThara Gopinath 	genpd_unlock(genpd);
3351b6a1d093SThara Gopinath 	return ret;
3352b6a1d093SThara Gopinath }
3353b6a1d093SThara Gopinath 
perf_state_show(struct seq_file * s,void * data)3354d32dcc6cSYangtao Li static int perf_state_show(struct seq_file *s, void *data)
3355e8912812SRajendra Nayak {
3356e8912812SRajendra Nayak 	struct generic_pm_domain *genpd = s->private;
3357e8912812SRajendra Nayak 
3358e8912812SRajendra Nayak 	if (genpd_lock_interruptible(genpd))
3359e8912812SRajendra Nayak 		return -ERESTARTSYS;
3360e8912812SRajendra Nayak 
3361e8912812SRajendra Nayak 	seq_printf(s, "%u\n", genpd->performance_state);
3362e8912812SRajendra Nayak 
3363e8912812SRajendra Nayak 	genpd_unlock(genpd);
3364e8912812SRajendra Nayak 	return 0;
3365e8912812SRajendra Nayak }
3366e8912812SRajendra Nayak 
3367d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(summary);
3368d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(status);
3369d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(sub_domains);
3370d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(idle_states);
3371d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(active_time);
3372d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(total_idle_time);
3373d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(devices);
3374d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(perf_state);
33752bd5306aSMaciej Matraszek 
genpd_debug_add(struct generic_pm_domain * genpd)3376718072ceSThierry Strudel static void genpd_debug_add(struct generic_pm_domain *genpd)
33772bd5306aSMaciej Matraszek {
33782bd5306aSMaciej Matraszek 	struct dentry *d;
33792bd5306aSMaciej Matraszek 
3380718072ceSThierry Strudel 	if (!genpd_debugfs_dir)
3381718072ceSThierry Strudel 		return;
33822bd5306aSMaciej Matraszek 
33839e9704eaSUlf Hansson 	d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
3384b6a1d093SThara Gopinath 
3385b6a1d093SThara Gopinath 	debugfs_create_file("current_state", 0444,
3386d32dcc6cSYangtao Li 			    d, genpd, &status_fops);
3387b6a1d093SThara Gopinath 	debugfs_create_file("sub_domains", 0444,
3388d32dcc6cSYangtao Li 			    d, genpd, &sub_domains_fops);
3389b6a1d093SThara Gopinath 	debugfs_create_file("idle_states", 0444,
3390d32dcc6cSYangtao Li 			    d, genpd, &idle_states_fops);
3391b6a1d093SThara Gopinath 	debugfs_create_file("active_time", 0444,
3392d32dcc6cSYangtao Li 			    d, genpd, &active_time_fops);
3393b6a1d093SThara Gopinath 	debugfs_create_file("total_idle_time", 0444,
3394d32dcc6cSYangtao Li 			    d, genpd, &total_idle_time_fops);
3395b6a1d093SThara Gopinath 	debugfs_create_file("devices", 0444,
3396d32dcc6cSYangtao Li 			    d, genpd, &devices_fops);
3397e8912812SRajendra Nayak 	if (genpd->set_performance_state)
3398e8912812SRajendra Nayak 		debugfs_create_file("perf_state", 0444,
3399d32dcc6cSYangtao Li 				    d, genpd, &perf_state_fops);
3400b6a1d093SThara Gopinath }
3401b6a1d093SThara Gopinath 
genpd_debug_init(void)3402718072ceSThierry Strudel static int __init genpd_debug_init(void)
3403718072ceSThierry Strudel {
3404718072ceSThierry Strudel 	struct generic_pm_domain *genpd;
3405718072ceSThierry Strudel 
3406718072ceSThierry Strudel 	genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
3407718072ceSThierry Strudel 
3408718072ceSThierry Strudel 	debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir,
3409718072ceSThierry Strudel 			    NULL, &summary_fops);
3410718072ceSThierry Strudel 
3411718072ceSThierry Strudel 	list_for_each_entry(genpd, &gpd_list, gpd_list_node)
3412718072ceSThierry Strudel 		genpd_debug_add(genpd);
3413718072ceSThierry Strudel 
34142bd5306aSMaciej Matraszek 	return 0;
34152bd5306aSMaciej Matraszek }
34169e9704eaSUlf Hansson late_initcall(genpd_debug_init);
34172bd5306aSMaciej Matraszek 
genpd_debug_exit(void)34189e9704eaSUlf Hansson static void __exit genpd_debug_exit(void)
34192bd5306aSMaciej Matraszek {
34209e9704eaSUlf Hansson 	debugfs_remove_recursive(genpd_debugfs_dir);
34212bd5306aSMaciej Matraszek }
34229e9704eaSUlf Hansson __exitcall(genpd_debug_exit);
34238b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */
3424