xref: /openbmc/linux/drivers/base/power/domain.c (revision 18b4f3f5)
1f721889fSRafael J. Wysocki /*
2f721889fSRafael J. Wysocki  * drivers/base/power/domain.c - Common code related to device power domains.
3f721889fSRafael J. Wysocki  *
4f721889fSRafael J. Wysocki  * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
5f721889fSRafael J. Wysocki  *
6f721889fSRafael J. Wysocki  * This file is released under the GPLv2.
7f721889fSRafael J. Wysocki  */
8f721889fSRafael J. Wysocki 
9f721889fSRafael J. Wysocki #include <linux/init.h>
10f721889fSRafael J. Wysocki #include <linux/kernel.h>
11f721889fSRafael J. Wysocki #include <linux/io.h>
12f721889fSRafael J. Wysocki #include <linux/pm_runtime.h>
13f721889fSRafael J. Wysocki #include <linux/pm_domain.h>
14f721889fSRafael J. Wysocki #include <linux/slab.h>
15f721889fSRafael J. Wysocki #include <linux/err.h>
16f721889fSRafael J. Wysocki 
175248051bSRafael J. Wysocki #ifdef CONFIG_PM
185248051bSRafael J. Wysocki 
195248051bSRafael J. Wysocki static struct generic_pm_domain *dev_to_genpd(struct device *dev)
205248051bSRafael J. Wysocki {
215248051bSRafael J. Wysocki 	if (IS_ERR_OR_NULL(dev->pm_domain))
225248051bSRafael J. Wysocki 		return ERR_PTR(-EINVAL);
235248051bSRafael J. Wysocki 
24596ba34bSRafael J. Wysocki 	return pd_to_genpd(dev->pm_domain);
255248051bSRafael J. Wysocki }
26f721889fSRafael J. Wysocki 
27f721889fSRafael J. Wysocki static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
28f721889fSRafael J. Wysocki {
29f721889fSRafael J. Wysocki 	if (!WARN_ON(genpd->sd_count == 0))
30f721889fSRafael J. Wysocki 			genpd->sd_count--;
31f721889fSRafael J. Wysocki }
32f721889fSRafael J. Wysocki 
33f721889fSRafael J. Wysocki /**
345248051bSRafael J. Wysocki  * pm_genpd_poweron - Restore power to a given PM domain and its parents.
355248051bSRafael J. Wysocki  * @genpd: PM domain to power up.
365248051bSRafael J. Wysocki  *
375248051bSRafael J. Wysocki  * Restore power to @genpd and all of its parents so that it is possible to
385248051bSRafael J. Wysocki  * resume a device belonging to it.
395248051bSRafael J. Wysocki  */
4018b4f3f5SMagnus Damm int pm_genpd_poweron(struct generic_pm_domain *genpd)
415248051bSRafael J. Wysocki {
425248051bSRafael J. Wysocki 	int ret = 0;
435248051bSRafael J. Wysocki 
445248051bSRafael J. Wysocki  start:
455248051bSRafael J. Wysocki 	if (genpd->parent)
465248051bSRafael J. Wysocki 		mutex_lock(&genpd->parent->lock);
475248051bSRafael J. Wysocki 	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
485248051bSRafael J. Wysocki 
49596ba34bSRafael J. Wysocki 	if (!genpd->power_is_off
50596ba34bSRafael J. Wysocki 	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
515248051bSRafael J. Wysocki 		goto out;
525248051bSRafael J. Wysocki 
535248051bSRafael J. Wysocki 	if (genpd->parent && genpd->parent->power_is_off) {
545248051bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
555248051bSRafael J. Wysocki 		mutex_unlock(&genpd->parent->lock);
565248051bSRafael J. Wysocki 
575248051bSRafael J. Wysocki 		ret = pm_genpd_poweron(genpd->parent);
585248051bSRafael J. Wysocki 		if (ret)
595248051bSRafael J. Wysocki 			return ret;
605248051bSRafael J. Wysocki 
615248051bSRafael J. Wysocki 		goto start;
625248051bSRafael J. Wysocki 	}
635248051bSRafael J. Wysocki 
645248051bSRafael J. Wysocki 	if (genpd->power_on) {
655248051bSRafael J. Wysocki 		int ret = genpd->power_on(genpd);
665248051bSRafael J. Wysocki 		if (ret)
675248051bSRafael J. Wysocki 			goto out;
685248051bSRafael J. Wysocki 	}
695248051bSRafael J. Wysocki 
705248051bSRafael J. Wysocki 	genpd->power_is_off = false;
715248051bSRafael J. Wysocki 	if (genpd->parent)
725248051bSRafael J. Wysocki 		genpd->parent->sd_count++;
735248051bSRafael J. Wysocki 
745248051bSRafael J. Wysocki  out:
755248051bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
765248051bSRafael J. Wysocki 	if (genpd->parent)
775248051bSRafael J. Wysocki 		mutex_unlock(&genpd->parent->lock);
785248051bSRafael J. Wysocki 
795248051bSRafael J. Wysocki 	return ret;
805248051bSRafael J. Wysocki }
815248051bSRafael J. Wysocki 
825248051bSRafael J. Wysocki #endif /* CONFIG_PM */
835248051bSRafael J. Wysocki 
845248051bSRafael J. Wysocki #ifdef CONFIG_PM_RUNTIME
855248051bSRafael J. Wysocki 
865248051bSRafael J. Wysocki /**
87f721889fSRafael J. Wysocki  * __pm_genpd_save_device - Save the pre-suspend state of a device.
88f721889fSRafael J. Wysocki  * @dle: Device list entry of the device to save the state of.
89f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
90f721889fSRafael J. Wysocki  */
91f721889fSRafael J. Wysocki static int __pm_genpd_save_device(struct dev_list_entry *dle,
92f721889fSRafael J. Wysocki 				  struct generic_pm_domain *genpd)
93f721889fSRafael J. Wysocki {
94f721889fSRafael J. Wysocki 	struct device *dev = dle->dev;
95f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
96f721889fSRafael J. Wysocki 	int ret = 0;
97f721889fSRafael J. Wysocki 
98f721889fSRafael J. Wysocki 	if (dle->need_restore)
99f721889fSRafael J. Wysocki 		return 0;
100f721889fSRafael J. Wysocki 
101f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_suspend) {
102f721889fSRafael J. Wysocki 		if (genpd->start_device)
103f721889fSRafael J. Wysocki 			genpd->start_device(dev);
104f721889fSRafael J. Wysocki 
105f721889fSRafael J. Wysocki 		ret = drv->pm->runtime_suspend(dev);
106f721889fSRafael J. Wysocki 
107f721889fSRafael J. Wysocki 		if (genpd->stop_device)
108f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
109f721889fSRafael J. Wysocki 	}
110f721889fSRafael J. Wysocki 
111f721889fSRafael J. Wysocki 	if (!ret)
112f721889fSRafael J. Wysocki 		dle->need_restore = true;
113f721889fSRafael J. Wysocki 
114f721889fSRafael J. Wysocki 	return ret;
115f721889fSRafael J. Wysocki }
116f721889fSRafael J. Wysocki 
117f721889fSRafael J. Wysocki /**
118f721889fSRafael J. Wysocki  * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
119f721889fSRafael J. Wysocki  * @dle: Device list entry of the device to restore the state of.
120f721889fSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
121f721889fSRafael J. Wysocki  */
122f721889fSRafael J. Wysocki static void __pm_genpd_restore_device(struct dev_list_entry *dle,
123f721889fSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
124f721889fSRafael J. Wysocki {
125f721889fSRafael J. Wysocki 	struct device *dev = dle->dev;
126f721889fSRafael J. Wysocki 	struct device_driver *drv = dev->driver;
127f721889fSRafael J. Wysocki 
128f721889fSRafael J. Wysocki 	if (!dle->need_restore)
129f721889fSRafael J. Wysocki 		return;
130f721889fSRafael J. Wysocki 
131f721889fSRafael J. Wysocki 	if (drv && drv->pm && drv->pm->runtime_resume) {
132f721889fSRafael J. Wysocki 		if (genpd->start_device)
133f721889fSRafael J. Wysocki 			genpd->start_device(dev);
134f721889fSRafael J. Wysocki 
135f721889fSRafael J. Wysocki 		drv->pm->runtime_resume(dev);
136f721889fSRafael J. Wysocki 
137f721889fSRafael J. Wysocki 		if (genpd->stop_device)
138f721889fSRafael J. Wysocki 			genpd->stop_device(dev);
139f721889fSRafael J. Wysocki 	}
140f721889fSRafael J. Wysocki 
141f721889fSRafael J. Wysocki 	dle->need_restore = false;
142f721889fSRafael J. Wysocki }
143f721889fSRafael J. Wysocki 
144f721889fSRafael J. Wysocki /**
145f721889fSRafael J. Wysocki  * pm_genpd_poweroff - Remove power from a given PM domain.
146f721889fSRafael J. Wysocki  * @genpd: PM domain to power down.
147f721889fSRafael J. Wysocki  *
148f721889fSRafael J. Wysocki  * If all of the @genpd's devices have been suspended and all of its subdomains
149f721889fSRafael J. Wysocki  * have been powered down, run the runtime suspend callbacks provided by all of
150f721889fSRafael J. Wysocki  * the @genpd's devices' drivers and remove power from @genpd.
151f721889fSRafael J. Wysocki  */
152f721889fSRafael J. Wysocki static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
153f721889fSRafael J. Wysocki {
154f721889fSRafael J. Wysocki 	struct generic_pm_domain *parent;
155f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
156f721889fSRafael J. Wysocki 	unsigned int not_suspended;
157f721889fSRafael J. Wysocki 	int ret;
158f721889fSRafael J. Wysocki 
159596ba34bSRafael J. Wysocki 	if (genpd->power_is_off || genpd->prepared_count > 0)
160f721889fSRafael J. Wysocki 		return 0;
161f721889fSRafael J. Wysocki 
162f721889fSRafael J. Wysocki 	if (genpd->sd_count > 0)
163f721889fSRafael J. Wysocki 		return -EBUSY;
164f721889fSRafael J. Wysocki 
165f721889fSRafael J. Wysocki 	not_suspended = 0;
166f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node)
167f721889fSRafael J. Wysocki 		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
168f721889fSRafael J. Wysocki 			not_suspended++;
169f721889fSRafael J. Wysocki 
170f721889fSRafael J. Wysocki 	if (not_suspended > genpd->in_progress)
171f721889fSRafael J. Wysocki 		return -EBUSY;
172f721889fSRafael J. Wysocki 
173f721889fSRafael J. Wysocki 	if (genpd->gov && genpd->gov->power_down_ok) {
174f721889fSRafael J. Wysocki 		if (!genpd->gov->power_down_ok(&genpd->domain))
175f721889fSRafael J. Wysocki 			return -EAGAIN;
176f721889fSRafael J. Wysocki 	}
177f721889fSRafael J. Wysocki 
178f721889fSRafael J. Wysocki 	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
179f721889fSRafael J. Wysocki 		ret = __pm_genpd_save_device(dle, genpd);
180f721889fSRafael J. Wysocki 		if (ret)
181f721889fSRafael J. Wysocki 			goto err_dev;
182f721889fSRafael J. Wysocki 	}
183f721889fSRafael J. Wysocki 
184f721889fSRafael J. Wysocki 	if (genpd->power_off)
185f721889fSRafael J. Wysocki 		genpd->power_off(genpd);
186f721889fSRafael J. Wysocki 
187f721889fSRafael J. Wysocki 	genpd->power_is_off = true;
188f721889fSRafael J. Wysocki 
189f721889fSRafael J. Wysocki 	parent = genpd->parent;
190f721889fSRafael J. Wysocki 	if (parent) {
191f721889fSRafael J. Wysocki 		genpd_sd_counter_dec(parent);
192f721889fSRafael J. Wysocki 		if (parent->sd_count == 0)
193f721889fSRafael J. Wysocki 			queue_work(pm_wq, &parent->power_off_work);
194f721889fSRafael J. Wysocki 	}
195f721889fSRafael J. Wysocki 
196f721889fSRafael J. Wysocki 	return 0;
197f721889fSRafael J. Wysocki 
198f721889fSRafael J. Wysocki  err_dev:
199f721889fSRafael J. Wysocki 	list_for_each_entry_continue(dle, &genpd->dev_list, node)
200f721889fSRafael J. Wysocki 		__pm_genpd_restore_device(dle, genpd);
201f721889fSRafael J. Wysocki 
202f721889fSRafael J. Wysocki 	return ret;
203f721889fSRafael J. Wysocki }
204f721889fSRafael J. Wysocki 
205f721889fSRafael J. Wysocki /**
206f721889fSRafael J. Wysocki  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
207f721889fSRafael J. Wysocki  * @work: Work structure used for scheduling the execution of this function.
208f721889fSRafael J. Wysocki  */
209f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work)
210f721889fSRafael J. Wysocki {
211f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
212f721889fSRafael J. Wysocki 
213f721889fSRafael J. Wysocki 	genpd = container_of(work, struct generic_pm_domain, power_off_work);
214f721889fSRafael J. Wysocki 
215f721889fSRafael J. Wysocki 	if (genpd->parent)
216f721889fSRafael J. Wysocki 		mutex_lock(&genpd->parent->lock);
217f721889fSRafael J. Wysocki 	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
218f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
219f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
220f721889fSRafael J. Wysocki 	if (genpd->parent)
221f721889fSRafael J. Wysocki 		mutex_unlock(&genpd->parent->lock);
222f721889fSRafael J. Wysocki }
223f721889fSRafael J. Wysocki 
224f721889fSRafael J. Wysocki /**
225f721889fSRafael J. Wysocki  * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
226f721889fSRafael J. Wysocki  * @dev: Device to suspend.
227f721889fSRafael J. Wysocki  *
228f721889fSRafael J. Wysocki  * Carry out a runtime suspend of a device under the assumption that its
229f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
230f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
231f721889fSRafael J. Wysocki  */
232f721889fSRafael J. Wysocki static int pm_genpd_runtime_suspend(struct device *dev)
233f721889fSRafael J. Wysocki {
234f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
235f721889fSRafael J. Wysocki 
236f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
237f721889fSRafael J. Wysocki 
2385248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
2395248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
240f721889fSRafael J. Wysocki 		return -EINVAL;
241f721889fSRafael J. Wysocki 
242f721889fSRafael J. Wysocki 	if (genpd->parent)
243f721889fSRafael J. Wysocki 		mutex_lock(&genpd->parent->lock);
244f721889fSRafael J. Wysocki 	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
245f721889fSRafael J. Wysocki 
246f721889fSRafael J. Wysocki 	if (genpd->stop_device) {
247f721889fSRafael J. Wysocki 		int ret = genpd->stop_device(dev);
248f721889fSRafael J. Wysocki 		if (ret)
249f721889fSRafael J. Wysocki 			goto out;
250f721889fSRafael J. Wysocki 	}
251f721889fSRafael J. Wysocki 	genpd->in_progress++;
252f721889fSRafael J. Wysocki 	pm_genpd_poweroff(genpd);
253f721889fSRafael J. Wysocki 	genpd->in_progress--;
254f721889fSRafael J. Wysocki 
255f721889fSRafael J. Wysocki  out:
256f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
257f721889fSRafael J. Wysocki 	if (genpd->parent)
258f721889fSRafael J. Wysocki 		mutex_unlock(&genpd->parent->lock);
259f721889fSRafael J. Wysocki 
260f721889fSRafael J. Wysocki 	return 0;
261f721889fSRafael J. Wysocki }
262f721889fSRafael J. Wysocki 
263f721889fSRafael J. Wysocki /**
264596ba34bSRafael J. Wysocki  * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
265596ba34bSRafael J. Wysocki  * @dev: Device to resume.
266596ba34bSRafael J. Wysocki  * @genpd: PM domain the device belongs to.
267596ba34bSRafael J. Wysocki  */
268596ba34bSRafael J. Wysocki static void __pm_genpd_runtime_resume(struct device *dev,
269596ba34bSRafael J. Wysocki 				      struct generic_pm_domain *genpd)
270596ba34bSRafael J. Wysocki {
271596ba34bSRafael J. Wysocki 	struct dev_list_entry *dle;
272596ba34bSRafael J. Wysocki 
273596ba34bSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node) {
274596ba34bSRafael J. Wysocki 		if (dle->dev == dev) {
275596ba34bSRafael J. Wysocki 			__pm_genpd_restore_device(dle, genpd);
276596ba34bSRafael J. Wysocki 			break;
277596ba34bSRafael J. Wysocki 		}
278596ba34bSRafael J. Wysocki 	}
279596ba34bSRafael J. Wysocki 
280596ba34bSRafael J. Wysocki 	if (genpd->start_device)
281596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
282596ba34bSRafael J. Wysocki }
283596ba34bSRafael J. Wysocki 
284596ba34bSRafael J. Wysocki /**
285f721889fSRafael J. Wysocki  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
286f721889fSRafael J. Wysocki  * @dev: Device to resume.
287f721889fSRafael J. Wysocki  *
288f721889fSRafael J. Wysocki  * Carry out a runtime resume of a device under the assumption that its
289f721889fSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
290f721889fSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
291f721889fSRafael J. Wysocki  */
292f721889fSRafael J. Wysocki static int pm_genpd_runtime_resume(struct device *dev)
293f721889fSRafael J. Wysocki {
294f721889fSRafael J. Wysocki 	struct generic_pm_domain *genpd;
295f721889fSRafael J. Wysocki 	int ret;
296f721889fSRafael J. Wysocki 
297f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
298f721889fSRafael J. Wysocki 
2995248051bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
3005248051bSRafael J. Wysocki 	if (IS_ERR(genpd))
301f721889fSRafael J. Wysocki 		return -EINVAL;
302f721889fSRafael J. Wysocki 
303f721889fSRafael J. Wysocki 	ret = pm_genpd_poweron(genpd);
304f721889fSRafael J. Wysocki 	if (ret)
305f721889fSRafael J. Wysocki 		return ret;
306f721889fSRafael J. Wysocki 
307f721889fSRafael J. Wysocki 	mutex_lock(&genpd->lock);
308596ba34bSRafael J. Wysocki 	__pm_genpd_runtime_resume(dev, genpd);
309f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
310f721889fSRafael J. Wysocki 
311f721889fSRafael J. Wysocki 	return 0;
312f721889fSRafael J. Wysocki }
313f721889fSRafael J. Wysocki 
314f721889fSRafael J. Wysocki #else
315f721889fSRafael J. Wysocki 
316f721889fSRafael J. Wysocki static inline void genpd_power_off_work_fn(struct work_struct *work) {}
317596ba34bSRafael J. Wysocki static inline void __pm_genpd_runtime_resume(struct device *dev,
318596ba34bSRafael J. Wysocki 					     struct generic_pm_domain *genpd) {}
319f721889fSRafael J. Wysocki 
320f721889fSRafael J. Wysocki #define pm_genpd_runtime_suspend	NULL
321f721889fSRafael J. Wysocki #define pm_genpd_runtime_resume		NULL
322f721889fSRafael J. Wysocki 
323f721889fSRafael J. Wysocki #endif /* CONFIG_PM_RUNTIME */
324f721889fSRafael J. Wysocki 
325596ba34bSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
326596ba34bSRafael J. Wysocki 
327596ba34bSRafael J. Wysocki /**
328596ba34bSRafael J. Wysocki  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
329596ba34bSRafael J. Wysocki  * @genpd: PM domain to power off, if possible.
330596ba34bSRafael J. Wysocki  *
331596ba34bSRafael J. Wysocki  * Check if the given PM domain can be powered off (during system suspend or
332596ba34bSRafael J. Wysocki  * hibernation) and do that if so.  Also, in that case propagate to its parent.
333596ba34bSRafael J. Wysocki  *
334596ba34bSRafael J. Wysocki  * This function is only called in "noirq" stages of system power transitions,
335596ba34bSRafael J. Wysocki  * so it need not acquire locks (all of the "noirq" callbacks are executed
336596ba34bSRafael J. Wysocki  * sequentially, so it is guaranteed that it will never run twice in parallel).
337596ba34bSRafael J. Wysocki  */
338596ba34bSRafael J. Wysocki static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
339596ba34bSRafael J. Wysocki {
340596ba34bSRafael J. Wysocki 	struct generic_pm_domain *parent = genpd->parent;
341596ba34bSRafael J. Wysocki 
342596ba34bSRafael J. Wysocki 	if (genpd->power_is_off)
343596ba34bSRafael J. Wysocki 		return;
344596ba34bSRafael J. Wysocki 
345596ba34bSRafael J. Wysocki 	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
346596ba34bSRafael J. Wysocki 		return;
347596ba34bSRafael J. Wysocki 
348596ba34bSRafael J. Wysocki 	if (genpd->power_off)
349596ba34bSRafael J. Wysocki 		genpd->power_off(genpd);
350596ba34bSRafael J. Wysocki 
351596ba34bSRafael J. Wysocki 	genpd->power_is_off = true;
352596ba34bSRafael J. Wysocki 	if (parent) {
353596ba34bSRafael J. Wysocki 		genpd_sd_counter_dec(parent);
354596ba34bSRafael J. Wysocki 		pm_genpd_sync_poweroff(parent);
355596ba34bSRafael J. Wysocki 	}
356596ba34bSRafael J. Wysocki }
357596ba34bSRafael J. Wysocki 
358596ba34bSRafael J. Wysocki /**
359596ba34bSRafael J. Wysocki  * pm_genpd_prepare - Start power transition of a device in a PM domain.
360596ba34bSRafael J. Wysocki  * @dev: Device to start the transition of.
361596ba34bSRafael J. Wysocki  *
362596ba34bSRafael J. Wysocki  * Start a power transition of a device (during a system-wide power transition)
363596ba34bSRafael J. Wysocki  * under the assumption that its pm_domain field points to the domain member of
364596ba34bSRafael J. Wysocki  * an object of type struct generic_pm_domain representing a PM domain
365596ba34bSRafael J. Wysocki  * consisting of I/O devices.
366596ba34bSRafael J. Wysocki  */
367596ba34bSRafael J. Wysocki static int pm_genpd_prepare(struct device *dev)
368596ba34bSRafael J. Wysocki {
369596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
370596ba34bSRafael J. Wysocki 
371596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
372596ba34bSRafael J. Wysocki 
373596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
374596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
375596ba34bSRafael J. Wysocki 		return -EINVAL;
376596ba34bSRafael J. Wysocki 
377596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
378596ba34bSRafael J. Wysocki 
379596ba34bSRafael J. Wysocki 	if (genpd->prepared_count++ == 0)
380596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = genpd->power_is_off;
381596ba34bSRafael J. Wysocki 
382596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
383596ba34bSRafael J. Wysocki 		mutex_unlock(&genpd->lock);
384596ba34bSRafael J. Wysocki 		return 0;
385596ba34bSRafael J. Wysocki 	}
386596ba34bSRafael J. Wysocki 
387596ba34bSRafael J. Wysocki 	/*
388596ba34bSRafael J. Wysocki 	 * If the device is in the (runtime) "suspended" state, call
389596ba34bSRafael J. Wysocki 	 * .start_device() for it, if defined.
390596ba34bSRafael J. Wysocki 	 */
391596ba34bSRafael J. Wysocki 	if (pm_runtime_suspended(dev))
392596ba34bSRafael J. Wysocki 		__pm_genpd_runtime_resume(dev, genpd);
393596ba34bSRafael J. Wysocki 
394596ba34bSRafael J. Wysocki 	/*
395596ba34bSRafael J. Wysocki 	 * Do not check if runtime resume is pending at this point, because it
396596ba34bSRafael J. Wysocki 	 * has been taken care of already and if pm_genpd_poweron() ran at this
397596ba34bSRafael J. Wysocki 	 * point as a result of the check, it would deadlock.
398596ba34bSRafael J. Wysocki 	 */
399596ba34bSRafael J. Wysocki 	__pm_runtime_disable(dev, false);
400596ba34bSRafael J. Wysocki 
401596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
402596ba34bSRafael J. Wysocki 
403596ba34bSRafael J. Wysocki 	return pm_generic_prepare(dev);
404596ba34bSRafael J. Wysocki }
405596ba34bSRafael J. Wysocki 
406596ba34bSRafael J. Wysocki /**
407596ba34bSRafael J. Wysocki  * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
408596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
409596ba34bSRafael J. Wysocki  *
410596ba34bSRafael J. Wysocki  * Suspend a device under the assumption that its pm_domain field points to the
411596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
412596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
413596ba34bSRafael J. Wysocki  */
414596ba34bSRafael J. Wysocki static int pm_genpd_suspend(struct device *dev)
415596ba34bSRafael J. Wysocki {
416596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
417596ba34bSRafael J. Wysocki 
418596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
419596ba34bSRafael J. Wysocki 
420596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
421596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
422596ba34bSRafael J. Wysocki 		return -EINVAL;
423596ba34bSRafael J. Wysocki 
424596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
425596ba34bSRafael J. Wysocki }
426596ba34bSRafael J. Wysocki 
427596ba34bSRafael J. Wysocki /**
428596ba34bSRafael J. Wysocki  * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
429596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
430596ba34bSRafael J. Wysocki  *
431596ba34bSRafael J. Wysocki  * Carry out a late suspend of a device under the assumption that its
432596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
433596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
434596ba34bSRafael J. Wysocki  */
435596ba34bSRafael J. Wysocki static int pm_genpd_suspend_noirq(struct device *dev)
436596ba34bSRafael J. Wysocki {
437596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
438596ba34bSRafael J. Wysocki 	int ret;
439596ba34bSRafael J. Wysocki 
440596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
441596ba34bSRafael J. Wysocki 
442596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
443596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
444596ba34bSRafael J. Wysocki 		return -EINVAL;
445596ba34bSRafael J. Wysocki 
446596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
447596ba34bSRafael J. Wysocki 		return 0;
448596ba34bSRafael J. Wysocki 
449596ba34bSRafael J. Wysocki 	ret = pm_generic_suspend_noirq(dev);
450596ba34bSRafael J. Wysocki 	if (ret)
451596ba34bSRafael J. Wysocki 		return ret;
452596ba34bSRafael J. Wysocki 
453d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
454d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
455d4f2d87aSRafael J. Wysocki 		return 0;
456d4f2d87aSRafael J. Wysocki 
457596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
458596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
459596ba34bSRafael J. Wysocki 
460596ba34bSRafael J. Wysocki 	/*
461596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
462596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
463596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
464596ba34bSRafael J. Wysocki 	 */
465596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
466596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
467596ba34bSRafael J. Wysocki 
468596ba34bSRafael J. Wysocki 	return 0;
469596ba34bSRafael J. Wysocki }
470596ba34bSRafael J. Wysocki 
471596ba34bSRafael J. Wysocki /**
472596ba34bSRafael J. Wysocki  * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
473596ba34bSRafael J. Wysocki  * @dev: Device to resume.
474596ba34bSRafael J. Wysocki  *
475596ba34bSRafael J. Wysocki  * Carry out an early resume of a device under the assumption that its
476596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
477596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
478596ba34bSRafael J. Wysocki  * devices.
479596ba34bSRafael J. Wysocki  */
480596ba34bSRafael J. Wysocki static int pm_genpd_resume_noirq(struct device *dev)
481596ba34bSRafael J. Wysocki {
482596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
483596ba34bSRafael J. Wysocki 
484596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
485596ba34bSRafael J. Wysocki 
486596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
487596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
488596ba34bSRafael J. Wysocki 		return -EINVAL;
489596ba34bSRafael J. Wysocki 
490596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
491596ba34bSRafael J. Wysocki 		return 0;
492596ba34bSRafael J. Wysocki 
493596ba34bSRafael J. Wysocki 	/*
494596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
495596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
496596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
497596ba34bSRafael J. Wysocki 	 */
498596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
499596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
500596ba34bSRafael J. Wysocki 	if (genpd->start_device)
501596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
502596ba34bSRafael J. Wysocki 
503596ba34bSRafael J. Wysocki 	return pm_generic_resume_noirq(dev);
504596ba34bSRafael J. Wysocki }
505596ba34bSRafael J. Wysocki 
506596ba34bSRafael J. Wysocki /**
507596ba34bSRafael J. Wysocki  * pm_genpd_resume - Resume a device belonging to an I/O power domain.
508596ba34bSRafael J. Wysocki  * @dev: Device to resume.
509596ba34bSRafael J. Wysocki  *
510596ba34bSRafael J. Wysocki  * Resume a device under the assumption that its pm_domain field points to the
511596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
512596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
513596ba34bSRafael J. Wysocki  */
514596ba34bSRafael J. Wysocki static int pm_genpd_resume(struct device *dev)
515596ba34bSRafael J. Wysocki {
516596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
517596ba34bSRafael J. Wysocki 
518596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
519596ba34bSRafael J. Wysocki 
520596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
521596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
522596ba34bSRafael J. Wysocki 		return -EINVAL;
523596ba34bSRafael J. Wysocki 
524596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
525596ba34bSRafael J. Wysocki }
526596ba34bSRafael J. Wysocki 
527596ba34bSRafael J. Wysocki /**
528596ba34bSRafael J. Wysocki  * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
529596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
530596ba34bSRafael J. Wysocki  *
531596ba34bSRafael J. Wysocki  * Freeze a device under the assumption that its pm_domain field points to the
532596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
533596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
534596ba34bSRafael J. Wysocki  */
535596ba34bSRafael J. Wysocki static int pm_genpd_freeze(struct device *dev)
536596ba34bSRafael J. Wysocki {
537596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
538596ba34bSRafael J. Wysocki 
539596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
540596ba34bSRafael J. Wysocki 
541596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
542596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
543596ba34bSRafael J. Wysocki 		return -EINVAL;
544596ba34bSRafael J. Wysocki 
545596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
546596ba34bSRafael J. Wysocki }
547596ba34bSRafael J. Wysocki 
548596ba34bSRafael J. Wysocki /**
549596ba34bSRafael J. Wysocki  * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
550596ba34bSRafael J. Wysocki  * @dev: Device to freeze.
551596ba34bSRafael J. Wysocki  *
552596ba34bSRafael J. Wysocki  * Carry out a late freeze of a device under the assumption that its
553596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
554596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
555596ba34bSRafael J. Wysocki  * devices.
556596ba34bSRafael J. Wysocki  */
557596ba34bSRafael J. Wysocki static int pm_genpd_freeze_noirq(struct device *dev)
558596ba34bSRafael J. Wysocki {
559596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
560596ba34bSRafael J. Wysocki 	int ret;
561596ba34bSRafael J. Wysocki 
562596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
563596ba34bSRafael J. Wysocki 
564596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
565596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
566596ba34bSRafael J. Wysocki 		return -EINVAL;
567596ba34bSRafael J. Wysocki 
568596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
569596ba34bSRafael J. Wysocki 		return 0;
570596ba34bSRafael J. Wysocki 
571596ba34bSRafael J. Wysocki 	ret = pm_generic_freeze_noirq(dev);
572596ba34bSRafael J. Wysocki 	if (ret)
573596ba34bSRafael J. Wysocki 		return ret;
574596ba34bSRafael J. Wysocki 
575596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
576596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
577596ba34bSRafael J. Wysocki 
578596ba34bSRafael J. Wysocki 	return 0;
579596ba34bSRafael J. Wysocki }
580596ba34bSRafael J. Wysocki 
581596ba34bSRafael J. Wysocki /**
582596ba34bSRafael J. Wysocki  * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
583596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
584596ba34bSRafael J. Wysocki  *
585596ba34bSRafael J. Wysocki  * Carry out an early thaw of a device under the assumption that its
586596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
587596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
588596ba34bSRafael J. Wysocki  * devices.
589596ba34bSRafael J. Wysocki  */
590596ba34bSRafael J. Wysocki static int pm_genpd_thaw_noirq(struct device *dev)
591596ba34bSRafael J. Wysocki {
592596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
593596ba34bSRafael J. Wysocki 
594596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
595596ba34bSRafael J. Wysocki 
596596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
597596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
598596ba34bSRafael J. Wysocki 		return -EINVAL;
599596ba34bSRafael J. Wysocki 
600596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
601596ba34bSRafael J. Wysocki 		return 0;
602596ba34bSRafael J. Wysocki 
603596ba34bSRafael J. Wysocki 	if (genpd->start_device)
604596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
605596ba34bSRafael J. Wysocki 
606596ba34bSRafael J. Wysocki 	return pm_generic_thaw_noirq(dev);
607596ba34bSRafael J. Wysocki }
608596ba34bSRafael J. Wysocki 
609596ba34bSRafael J. Wysocki /**
610596ba34bSRafael J. Wysocki  * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
611596ba34bSRafael J. Wysocki  * @dev: Device to thaw.
612596ba34bSRafael J. Wysocki  *
613596ba34bSRafael J. Wysocki  * Thaw a device under the assumption that its pm_domain field points to the
614596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
615596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
616596ba34bSRafael J. Wysocki  */
617596ba34bSRafael J. Wysocki static int pm_genpd_thaw(struct device *dev)
618596ba34bSRafael J. Wysocki {
619596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
620596ba34bSRafael J. Wysocki 
621596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
622596ba34bSRafael J. Wysocki 
623596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
624596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
625596ba34bSRafael J. Wysocki 		return -EINVAL;
626596ba34bSRafael J. Wysocki 
627596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
628596ba34bSRafael J. Wysocki }
629596ba34bSRafael J. Wysocki 
630596ba34bSRafael J. Wysocki /**
631596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
632596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
633596ba34bSRafael J. Wysocki  *
634596ba34bSRafael J. Wysocki  * Power off a device under the assumption that its pm_domain field points to
635596ba34bSRafael J. Wysocki  * the domain member of an object of type struct generic_pm_domain representing
636596ba34bSRafael J. Wysocki  * a PM domain consisting of I/O devices.
637596ba34bSRafael J. Wysocki  */
638596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff(struct device *dev)
639596ba34bSRafael J. Wysocki {
640596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
641596ba34bSRafael J. Wysocki 
642596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
643596ba34bSRafael J. Wysocki 
644596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
645596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
646596ba34bSRafael J. Wysocki 		return -EINVAL;
647596ba34bSRafael J. Wysocki 
648596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
649596ba34bSRafael J. Wysocki }
650596ba34bSRafael J. Wysocki 
651596ba34bSRafael J. Wysocki /**
652596ba34bSRafael J. Wysocki  * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
653596ba34bSRafael J. Wysocki  * @dev: Device to suspend.
654596ba34bSRafael J. Wysocki  *
655596ba34bSRafael J. Wysocki  * Carry out a late powering off of a device under the assumption that its
656596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
657596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a PM domain consisting of I/O devices.
658596ba34bSRafael J. Wysocki  */
659596ba34bSRafael J. Wysocki static int pm_genpd_dev_poweroff_noirq(struct device *dev)
660596ba34bSRafael J. Wysocki {
661596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
662596ba34bSRafael J. Wysocki 	int ret;
663596ba34bSRafael J. Wysocki 
664596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
665596ba34bSRafael J. Wysocki 
666596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
667596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
668596ba34bSRafael J. Wysocki 		return -EINVAL;
669596ba34bSRafael J. Wysocki 
670596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off)
671596ba34bSRafael J. Wysocki 		return 0;
672596ba34bSRafael J. Wysocki 
673596ba34bSRafael J. Wysocki 	ret = pm_generic_poweroff_noirq(dev);
674596ba34bSRafael J. Wysocki 	if (ret)
675596ba34bSRafael J. Wysocki 		return ret;
676596ba34bSRafael J. Wysocki 
677d4f2d87aSRafael J. Wysocki 	if (device_may_wakeup(dev)
678d4f2d87aSRafael J. Wysocki 	    && genpd->active_wakeup && genpd->active_wakeup(dev))
679d4f2d87aSRafael J. Wysocki 		return 0;
680d4f2d87aSRafael J. Wysocki 
681596ba34bSRafael J. Wysocki 	if (genpd->stop_device)
682596ba34bSRafael J. Wysocki 		genpd->stop_device(dev);
683596ba34bSRafael J. Wysocki 
684596ba34bSRafael J. Wysocki 	/*
685596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
686596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
687596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
688596ba34bSRafael J. Wysocki 	 */
689596ba34bSRafael J. Wysocki 	genpd->suspended_count++;
690596ba34bSRafael J. Wysocki 	pm_genpd_sync_poweroff(genpd);
691596ba34bSRafael J. Wysocki 
692596ba34bSRafael J. Wysocki 	return 0;
693596ba34bSRafael J. Wysocki }
694596ba34bSRafael J. Wysocki 
695596ba34bSRafael J. Wysocki /**
696596ba34bSRafael J. Wysocki  * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
697596ba34bSRafael J. Wysocki  * @dev: Device to resume.
698596ba34bSRafael J. Wysocki  *
699596ba34bSRafael J. Wysocki  * Carry out an early restore of a device under the assumption that its
700596ba34bSRafael J. Wysocki  * pm_domain field points to the domain member of an object of type
701596ba34bSRafael J. Wysocki  * struct generic_pm_domain representing a power domain consisting of I/O
702596ba34bSRafael J. Wysocki  * devices.
703596ba34bSRafael J. Wysocki  */
704596ba34bSRafael J. Wysocki static int pm_genpd_restore_noirq(struct device *dev)
705596ba34bSRafael J. Wysocki {
706596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
707596ba34bSRafael J. Wysocki 
708596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
709596ba34bSRafael J. Wysocki 
710596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
711596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
712596ba34bSRafael J. Wysocki 		return -EINVAL;
713596ba34bSRafael J. Wysocki 
714596ba34bSRafael J. Wysocki 	/*
715596ba34bSRafael J. Wysocki 	 * Since all of the "noirq" callbacks are executed sequentially, it is
716596ba34bSRafael J. Wysocki 	 * guaranteed that this function will never run twice in parallel for
717596ba34bSRafael J. Wysocki 	 * the same PM domain, so it is not necessary to use locking here.
718596ba34bSRafael J. Wysocki 	 */
719596ba34bSRafael J. Wysocki 	genpd->power_is_off = true;
720596ba34bSRafael J. Wysocki 	if (genpd->suspend_power_off) {
721596ba34bSRafael J. Wysocki 		/*
722596ba34bSRafael J. Wysocki 		 * The boot kernel might put the domain into the power on state,
723596ba34bSRafael J. Wysocki 		 * so make sure it really is powered off.
724596ba34bSRafael J. Wysocki 		 */
725596ba34bSRafael J. Wysocki 		if (genpd->power_off)
726596ba34bSRafael J. Wysocki 			genpd->power_off(genpd);
727596ba34bSRafael J. Wysocki 		return 0;
728596ba34bSRafael J. Wysocki 	}
729596ba34bSRafael J. Wysocki 
730596ba34bSRafael J. Wysocki 	pm_genpd_poweron(genpd);
731596ba34bSRafael J. Wysocki 	genpd->suspended_count--;
732596ba34bSRafael J. Wysocki 	if (genpd->start_device)
733596ba34bSRafael J. Wysocki 		genpd->start_device(dev);
734596ba34bSRafael J. Wysocki 
735596ba34bSRafael J. Wysocki 	return pm_generic_restore_noirq(dev);
736596ba34bSRafael J. Wysocki }
737596ba34bSRafael J. Wysocki 
738596ba34bSRafael J. Wysocki /**
739596ba34bSRafael J. Wysocki  * pm_genpd_restore - Restore a device belonging to an I/O power domain.
740596ba34bSRafael J. Wysocki  * @dev: Device to resume.
741596ba34bSRafael J. Wysocki  *
742596ba34bSRafael J. Wysocki  * Restore a device under the assumption that its pm_domain field points to the
743596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
744596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
745596ba34bSRafael J. Wysocki  */
746596ba34bSRafael J. Wysocki static int pm_genpd_restore(struct device *dev)
747596ba34bSRafael J. Wysocki {
748596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
749596ba34bSRafael J. Wysocki 
750596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
751596ba34bSRafael J. Wysocki 
752596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
753596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
754596ba34bSRafael J. Wysocki 		return -EINVAL;
755596ba34bSRafael J. Wysocki 
756596ba34bSRafael J. Wysocki 	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
757596ba34bSRafael J. Wysocki }
758596ba34bSRafael J. Wysocki 
759596ba34bSRafael J. Wysocki /**
760596ba34bSRafael J. Wysocki  * pm_genpd_complete - Complete power transition of a device in a power domain.
761596ba34bSRafael J. Wysocki  * @dev: Device to complete the transition of.
762596ba34bSRafael J. Wysocki  *
763596ba34bSRafael J. Wysocki  * Complete a power transition of a device (during a system-wide power
764596ba34bSRafael J. Wysocki  * transition) under the assumption that its pm_domain field points to the
765596ba34bSRafael J. Wysocki  * domain member of an object of type struct generic_pm_domain representing
766596ba34bSRafael J. Wysocki  * a power domain consisting of I/O devices.
767596ba34bSRafael J. Wysocki  */
768596ba34bSRafael J. Wysocki static void pm_genpd_complete(struct device *dev)
769596ba34bSRafael J. Wysocki {
770596ba34bSRafael J. Wysocki 	struct generic_pm_domain *genpd;
771596ba34bSRafael J. Wysocki 	bool run_complete;
772596ba34bSRafael J. Wysocki 
773596ba34bSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
774596ba34bSRafael J. Wysocki 
775596ba34bSRafael J. Wysocki 	genpd = dev_to_genpd(dev);
776596ba34bSRafael J. Wysocki 	if (IS_ERR(genpd))
777596ba34bSRafael J. Wysocki 		return;
778596ba34bSRafael J. Wysocki 
779596ba34bSRafael J. Wysocki 	mutex_lock(&genpd->lock);
780596ba34bSRafael J. Wysocki 
781596ba34bSRafael J. Wysocki 	run_complete = !genpd->suspend_power_off;
782596ba34bSRafael J. Wysocki 	if (--genpd->prepared_count == 0)
783596ba34bSRafael J. Wysocki 		genpd->suspend_power_off = false;
784596ba34bSRafael J. Wysocki 
785596ba34bSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
786596ba34bSRafael J. Wysocki 
787596ba34bSRafael J. Wysocki 	if (run_complete) {
788596ba34bSRafael J. Wysocki 		pm_generic_complete(dev);
789596ba34bSRafael J. Wysocki 		pm_runtime_enable(dev);
790596ba34bSRafael J. Wysocki 	}
791596ba34bSRafael J. Wysocki }
792596ba34bSRafael J. Wysocki 
793596ba34bSRafael J. Wysocki #else
794596ba34bSRafael J. Wysocki 
795596ba34bSRafael J. Wysocki #define pm_genpd_prepare		NULL
796596ba34bSRafael J. Wysocki #define pm_genpd_suspend		NULL
797596ba34bSRafael J. Wysocki #define pm_genpd_suspend_noirq		NULL
798596ba34bSRafael J. Wysocki #define pm_genpd_resume_noirq		NULL
799596ba34bSRafael J. Wysocki #define pm_genpd_resume			NULL
800596ba34bSRafael J. Wysocki #define pm_genpd_freeze			NULL
801596ba34bSRafael J. Wysocki #define pm_genpd_freeze_noirq		NULL
802596ba34bSRafael J. Wysocki #define pm_genpd_thaw_noirq		NULL
803596ba34bSRafael J. Wysocki #define pm_genpd_thaw			NULL
804596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff_noirq	NULL
805596ba34bSRafael J. Wysocki #define pm_genpd_dev_poweroff		NULL
806596ba34bSRafael J. Wysocki #define pm_genpd_restore_noirq		NULL
807596ba34bSRafael J. Wysocki #define pm_genpd_restore		NULL
808596ba34bSRafael J. Wysocki #define pm_genpd_complete		NULL
809596ba34bSRafael J. Wysocki 
810596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
811596ba34bSRafael J. Wysocki 
812f721889fSRafael J. Wysocki /**
813f721889fSRafael J. Wysocki  * pm_genpd_add_device - Add a device to an I/O PM domain.
814f721889fSRafael J. Wysocki  * @genpd: PM domain to add the device to.
815f721889fSRafael J. Wysocki  * @dev: Device to be added.
816f721889fSRafael J. Wysocki  */
817f721889fSRafael J. Wysocki int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
818f721889fSRafael J. Wysocki {
819f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
820f721889fSRafael J. Wysocki 	int ret = 0;
821f721889fSRafael J. Wysocki 
822f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
823f721889fSRafael J. Wysocki 
824f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
825f721889fSRafael J. Wysocki 		return -EINVAL;
826f721889fSRafael J. Wysocki 
827f721889fSRafael J. Wysocki 	mutex_lock(&genpd->lock);
828f721889fSRafael J. Wysocki 
829f721889fSRafael J. Wysocki 	if (genpd->power_is_off) {
830f721889fSRafael J. Wysocki 		ret = -EINVAL;
831f721889fSRafael J. Wysocki 		goto out;
832f721889fSRafael J. Wysocki 	}
833f721889fSRafael J. Wysocki 
834596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
835596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
836596ba34bSRafael J. Wysocki 		goto out;
837596ba34bSRafael J. Wysocki 	}
838596ba34bSRafael J. Wysocki 
839f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node)
840f721889fSRafael J. Wysocki 		if (dle->dev == dev) {
841f721889fSRafael J. Wysocki 			ret = -EINVAL;
842f721889fSRafael J. Wysocki 			goto out;
843f721889fSRafael J. Wysocki 		}
844f721889fSRafael J. Wysocki 
845f721889fSRafael J. Wysocki 	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
846f721889fSRafael J. Wysocki 	if (!dle) {
847f721889fSRafael J. Wysocki 		ret = -ENOMEM;
848f721889fSRafael J. Wysocki 		goto out;
849f721889fSRafael J. Wysocki 	}
850f721889fSRafael J. Wysocki 
851f721889fSRafael J. Wysocki 	dle->dev = dev;
852f721889fSRafael J. Wysocki 	dle->need_restore = false;
853f721889fSRafael J. Wysocki 	list_add_tail(&dle->node, &genpd->dev_list);
854596ba34bSRafael J. Wysocki 	genpd->device_count++;
855f721889fSRafael J. Wysocki 
856f721889fSRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
857f721889fSRafael J. Wysocki 	dev->pm_domain = &genpd->domain;
858f721889fSRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
859f721889fSRafael J. Wysocki 
860f721889fSRafael J. Wysocki  out:
861f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
862f721889fSRafael J. Wysocki 
863f721889fSRafael J. Wysocki 	return ret;
864f721889fSRafael J. Wysocki }
865f721889fSRafael J. Wysocki 
866f721889fSRafael J. Wysocki /**
867f721889fSRafael J. Wysocki  * pm_genpd_remove_device - Remove a device from an I/O PM domain.
868f721889fSRafael J. Wysocki  * @genpd: PM domain to remove the device from.
869f721889fSRafael J. Wysocki  * @dev: Device to be removed.
870f721889fSRafael J. Wysocki  */
871f721889fSRafael J. Wysocki int pm_genpd_remove_device(struct generic_pm_domain *genpd,
872f721889fSRafael J. Wysocki 			   struct device *dev)
873f721889fSRafael J. Wysocki {
874f721889fSRafael J. Wysocki 	struct dev_list_entry *dle;
875f721889fSRafael J. Wysocki 	int ret = -EINVAL;
876f721889fSRafael J. Wysocki 
877f721889fSRafael J. Wysocki 	dev_dbg(dev, "%s()\n", __func__);
878f721889fSRafael J. Wysocki 
879f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
880f721889fSRafael J. Wysocki 		return -EINVAL;
881f721889fSRafael J. Wysocki 
882f721889fSRafael J. Wysocki 	mutex_lock(&genpd->lock);
883f721889fSRafael J. Wysocki 
884596ba34bSRafael J. Wysocki 	if (genpd->prepared_count > 0) {
885596ba34bSRafael J. Wysocki 		ret = -EAGAIN;
886596ba34bSRafael J. Wysocki 		goto out;
887596ba34bSRafael J. Wysocki 	}
888596ba34bSRafael J. Wysocki 
889f721889fSRafael J. Wysocki 	list_for_each_entry(dle, &genpd->dev_list, node) {
890f721889fSRafael J. Wysocki 		if (dle->dev != dev)
891f721889fSRafael J. Wysocki 			continue;
892f721889fSRafael J. Wysocki 
893f721889fSRafael J. Wysocki 		spin_lock_irq(&dev->power.lock);
894f721889fSRafael J. Wysocki 		dev->pm_domain = NULL;
895f721889fSRafael J. Wysocki 		spin_unlock_irq(&dev->power.lock);
896f721889fSRafael J. Wysocki 
897596ba34bSRafael J. Wysocki 		genpd->device_count--;
898f721889fSRafael J. Wysocki 		list_del(&dle->node);
899f721889fSRafael J. Wysocki 		kfree(dle);
900f721889fSRafael J. Wysocki 
901f721889fSRafael J. Wysocki 		ret = 0;
902f721889fSRafael J. Wysocki 		break;
903f721889fSRafael J. Wysocki 	}
904f721889fSRafael J. Wysocki 
905596ba34bSRafael J. Wysocki  out:
906f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
907f721889fSRafael J. Wysocki 
908f721889fSRafael J. Wysocki 	return ret;
909f721889fSRafael J. Wysocki }
910f721889fSRafael J. Wysocki 
911f721889fSRafael J. Wysocki /**
912f721889fSRafael J. Wysocki  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
913f721889fSRafael J. Wysocki  * @genpd: Master PM domain to add the subdomain to.
914f721889fSRafael J. Wysocki  * @new_subdomain: Subdomain to be added.
915f721889fSRafael J. Wysocki  */
916f721889fSRafael J. Wysocki int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
917f721889fSRafael J. Wysocki 			   struct generic_pm_domain *new_subdomain)
918f721889fSRafael J. Wysocki {
919f721889fSRafael J. Wysocki 	struct generic_pm_domain *subdomain;
920f721889fSRafael J. Wysocki 	int ret = 0;
921f721889fSRafael J. Wysocki 
922f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
923f721889fSRafael J. Wysocki 		return -EINVAL;
924f721889fSRafael J. Wysocki 
925f721889fSRafael J. Wysocki 	mutex_lock(&genpd->lock);
926f721889fSRafael J. Wysocki 
927f721889fSRafael J. Wysocki 	if (genpd->power_is_off && !new_subdomain->power_is_off) {
928f721889fSRafael J. Wysocki 		ret = -EINVAL;
929f721889fSRafael J. Wysocki 		goto out;
930f721889fSRafael J. Wysocki 	}
931f721889fSRafael J. Wysocki 
932f721889fSRafael J. Wysocki 	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
933f721889fSRafael J. Wysocki 		if (subdomain == new_subdomain) {
934f721889fSRafael J. Wysocki 			ret = -EINVAL;
935f721889fSRafael J. Wysocki 			goto out;
936f721889fSRafael J. Wysocki 		}
937f721889fSRafael J. Wysocki 	}
938f721889fSRafael J. Wysocki 
939f721889fSRafael J. Wysocki 	mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
940f721889fSRafael J. Wysocki 
941f721889fSRafael J. Wysocki 	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
942f721889fSRafael J. Wysocki 	new_subdomain->parent = genpd;
943f721889fSRafael J. Wysocki 	if (!subdomain->power_is_off)
944f721889fSRafael J. Wysocki 		genpd->sd_count++;
945f721889fSRafael J. Wysocki 
946f721889fSRafael J. Wysocki 	mutex_unlock(&new_subdomain->lock);
947f721889fSRafael J. Wysocki 
948f721889fSRafael J. Wysocki  out:
949f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
950f721889fSRafael J. Wysocki 
951f721889fSRafael J. Wysocki 	return ret;
952f721889fSRafael J. Wysocki }
953f721889fSRafael J. Wysocki 
954f721889fSRafael J. Wysocki /**
955f721889fSRafael J. Wysocki  * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
956f721889fSRafael J. Wysocki  * @genpd: Master PM domain to remove the subdomain from.
957f721889fSRafael J. Wysocki  * @target: Subdomain to be removed.
958f721889fSRafael J. Wysocki  */
959f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
960f721889fSRafael J. Wysocki 			      struct generic_pm_domain *target)
961f721889fSRafael J. Wysocki {
962f721889fSRafael J. Wysocki 	struct generic_pm_domain *subdomain;
963f721889fSRafael J. Wysocki 	int ret = -EINVAL;
964f721889fSRafael J. Wysocki 
965f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
966f721889fSRafael J. Wysocki 		return -EINVAL;
967f721889fSRafael J. Wysocki 
968f721889fSRafael J. Wysocki 	mutex_lock(&genpd->lock);
969f721889fSRafael J. Wysocki 
970f721889fSRafael J. Wysocki 	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
971f721889fSRafael J. Wysocki 		if (subdomain != target)
972f721889fSRafael J. Wysocki 			continue;
973f721889fSRafael J. Wysocki 
974f721889fSRafael J. Wysocki 		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
975f721889fSRafael J. Wysocki 
976f721889fSRafael J. Wysocki 		list_del(&subdomain->sd_node);
977f721889fSRafael J. Wysocki 		subdomain->parent = NULL;
978f721889fSRafael J. Wysocki 		if (!subdomain->power_is_off)
979f721889fSRafael J. Wysocki 			genpd_sd_counter_dec(genpd);
980f721889fSRafael J. Wysocki 
981f721889fSRafael J. Wysocki 		mutex_unlock(&subdomain->lock);
982f721889fSRafael J. Wysocki 
983f721889fSRafael J. Wysocki 		ret = 0;
984f721889fSRafael J. Wysocki 		break;
985f721889fSRafael J. Wysocki 	}
986f721889fSRafael J. Wysocki 
987f721889fSRafael J. Wysocki 	mutex_unlock(&genpd->lock);
988f721889fSRafael J. Wysocki 
989f721889fSRafael J. Wysocki 	return ret;
990f721889fSRafael J. Wysocki }
991f721889fSRafael J. Wysocki 
992f721889fSRafael J. Wysocki /**
993f721889fSRafael J. Wysocki  * pm_genpd_init - Initialize a generic I/O PM domain object.
994f721889fSRafael J. Wysocki  * @genpd: PM domain object to initialize.
995f721889fSRafael J. Wysocki  * @gov: PM domain governor to associate with the domain (may be NULL).
996f721889fSRafael J. Wysocki  * @is_off: Initial value of the domain's power_is_off field.
997f721889fSRafael J. Wysocki  */
998f721889fSRafael J. Wysocki void pm_genpd_init(struct generic_pm_domain *genpd,
999f721889fSRafael J. Wysocki 		   struct dev_power_governor *gov, bool is_off)
1000f721889fSRafael J. Wysocki {
1001f721889fSRafael J. Wysocki 	if (IS_ERR_OR_NULL(genpd))
1002f721889fSRafael J. Wysocki 		return;
1003f721889fSRafael J. Wysocki 
1004f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->sd_node);
1005f721889fSRafael J. Wysocki 	genpd->parent = NULL;
1006f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->dev_list);
1007f721889fSRafael J. Wysocki 	INIT_LIST_HEAD(&genpd->sd_list);
1008f721889fSRafael J. Wysocki 	mutex_init(&genpd->lock);
1009f721889fSRafael J. Wysocki 	genpd->gov = gov;
1010f721889fSRafael J. Wysocki 	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
1011f721889fSRafael J. Wysocki 	genpd->in_progress = 0;
1012f721889fSRafael J. Wysocki 	genpd->sd_count = 0;
1013f721889fSRafael J. Wysocki 	genpd->power_is_off = is_off;
1014596ba34bSRafael J. Wysocki 	genpd->device_count = 0;
1015596ba34bSRafael J. Wysocki 	genpd->suspended_count = 0;
1016f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
1017f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
1018f721889fSRafael J. Wysocki 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
1019596ba34bSRafael J. Wysocki 	genpd->domain.ops.prepare = pm_genpd_prepare;
1020596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend = pm_genpd_suspend;
1021596ba34bSRafael J. Wysocki 	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
1022596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
1023596ba34bSRafael J. Wysocki 	genpd->domain.ops.resume = pm_genpd_resume;
1024596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze = pm_genpd_freeze;
1025596ba34bSRafael J. Wysocki 	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
1026596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
1027596ba34bSRafael J. Wysocki 	genpd->domain.ops.thaw = pm_genpd_thaw;
1028596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
1029596ba34bSRafael J. Wysocki 	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
1030596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
1031596ba34bSRafael J. Wysocki 	genpd->domain.ops.restore = pm_genpd_restore;
1032596ba34bSRafael J. Wysocki 	genpd->domain.ops.complete = pm_genpd_complete;
1033f721889fSRafael J. Wysocki }
1034