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