xref: /linux/drivers/base/power/generic_ops.c (revision 12871a0bd67dd4db4418e1daafcd46e9d329ef10)
1 /*
2  * drivers/base/power/generic_ops.c - Generic PM callbacks for subsystems
3  *
4  * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
5  *
6  * This file is released under the GPLv2.
7  */
8 
9 #include <linux/pm.h>
10 #include <linux/pm_runtime.h>
11 
12 #ifdef CONFIG_PM_RUNTIME
13 /**
14  * pm_generic_runtime_idle - Generic runtime idle callback for subsystems.
15  * @dev: Device to handle.
16  *
17  * If PM operations are defined for the @dev's driver and they include
18  * ->runtime_idle(), execute it and return its error code, if nonzero.
19  * Otherwise, execute pm_runtime_suspend() for the device and return 0.
20  */
21 int pm_generic_runtime_idle(struct device *dev)
22 {
23 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
24 
25 	if (pm && pm->runtime_idle) {
26 		int ret = pm->runtime_idle(dev);
27 		if (ret)
28 			return ret;
29 	}
30 
31 	pm_runtime_suspend(dev);
32 	return 0;
33 }
34 EXPORT_SYMBOL_GPL(pm_generic_runtime_idle);
35 
36 /**
37  * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
38  * @dev: Device to suspend.
39  *
40  * If PM operations are defined for the @dev's driver and they include
41  * ->runtime_suspend(), execute it and return its error code.  Otherwise,
42  * return 0.
43  */
44 int pm_generic_runtime_suspend(struct device *dev)
45 {
46 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
47 	int ret;
48 
49 	ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
50 
51 	return ret;
52 }
53 EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
54 
55 /**
56  * pm_generic_runtime_resume - Generic runtime resume callback for subsystems.
57  * @dev: Device to resume.
58  *
59  * If PM operations are defined for the @dev's driver and they include
60  * ->runtime_resume(), execute it and return its error code.  Otherwise,
61  * return 0.
62  */
63 int pm_generic_runtime_resume(struct device *dev)
64 {
65 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
66 	int ret;
67 
68 	ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
69 
70 	return ret;
71 }
72 EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
73 #endif /* CONFIG_PM_RUNTIME */
74 
75 #ifdef CONFIG_PM_SLEEP
76 /**
77  * pm_generic_prepare - Generic routine preparing a device for power transition.
78  * @dev: Device to prepare.
79  *
80  * Prepare a device for a system-wide power transition.
81  */
82 int pm_generic_prepare(struct device *dev)
83 {
84 	struct device_driver *drv = dev->driver;
85 	int ret = 0;
86 
87 	if (drv && drv->pm && drv->pm->prepare)
88 		ret = drv->pm->prepare(dev);
89 
90 	return ret;
91 }
92 
93 /**
94  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
95  * @dev: Device to handle.
96  * @event: PM transition of the system under way.
97  *
98  * If the device has not been suspended at run time, execute the
99  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
100  * return its error code.  Otherwise, return zero.
101  */
102 static int __pm_generic_call(struct device *dev, int event)
103 {
104 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
105 	int (*callback)(struct device *);
106 
107 	if (!pm || pm_runtime_suspended(dev))
108 		return 0;
109 
110 	switch (event) {
111 	case PM_EVENT_SUSPEND:
112 		callback = pm->suspend;
113 		break;
114 	case PM_EVENT_FREEZE:
115 		callback = pm->freeze;
116 		break;
117 	case PM_EVENT_HIBERNATE:
118 		callback = pm->poweroff;
119 		break;
120 	case PM_EVENT_THAW:
121 		callback = pm->thaw;
122 		break;
123 	default:
124 		callback = NULL;
125 		break;
126 	}
127 
128 	return callback ? callback(dev) : 0;
129 }
130 
131 /**
132  * pm_generic_suspend - Generic suspend callback for subsystems.
133  * @dev: Device to suspend.
134  */
135 int pm_generic_suspend(struct device *dev)
136 {
137 	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
138 }
139 EXPORT_SYMBOL_GPL(pm_generic_suspend);
140 
141 /**
142  * pm_generic_freeze - Generic freeze callback for subsystems.
143  * @dev: Device to freeze.
144  */
145 int pm_generic_freeze(struct device *dev)
146 {
147 	return __pm_generic_call(dev, PM_EVENT_FREEZE);
148 }
149 EXPORT_SYMBOL_GPL(pm_generic_freeze);
150 
151 /**
152  * pm_generic_poweroff - Generic poweroff callback for subsystems.
153  * @dev: Device to handle.
154  */
155 int pm_generic_poweroff(struct device *dev)
156 {
157 	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
158 }
159 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
160 
161 /**
162  * pm_generic_thaw - Generic thaw callback for subsystems.
163  * @dev: Device to thaw.
164  */
165 int pm_generic_thaw(struct device *dev)
166 {
167 	return __pm_generic_call(dev, PM_EVENT_THAW);
168 }
169 EXPORT_SYMBOL_GPL(pm_generic_thaw);
170 
171 /**
172  * __pm_generic_resume - Generic resume/restore callback for subsystems.
173  * @dev: Device to handle.
174  * @event: PM transition of the system under way.
175  *
176  * Execute the resume/resotre callback provided by the @dev's driver, if
177  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
178  * Return the callback's error code.
179  */
180 static int __pm_generic_resume(struct device *dev, int event)
181 {
182 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
183 	int (*callback)(struct device *);
184 	int ret;
185 
186 	if (!pm)
187 		return 0;
188 
189 	switch (event) {
190 	case PM_EVENT_RESUME:
191 		callback = pm->resume;
192 		break;
193 	case PM_EVENT_RESTORE:
194 		callback = pm->restore;
195 		break;
196 	default:
197 		callback = NULL;
198 		break;
199 	}
200 
201 	if (!callback)
202 		return 0;
203 
204 	ret = callback(dev);
205 	if (!ret && pm_runtime_enabled(dev)) {
206 		pm_runtime_disable(dev);
207 		pm_runtime_set_active(dev);
208 		pm_runtime_enable(dev);
209 	}
210 
211 	return ret;
212 }
213 
214 /**
215  * pm_generic_resume - Generic resume callback for subsystems.
216  * @dev: Device to resume.
217  */
218 int pm_generic_resume(struct device *dev)
219 {
220 	return __pm_generic_resume(dev, PM_EVENT_RESUME);
221 }
222 EXPORT_SYMBOL_GPL(pm_generic_resume);
223 
224 /**
225  * pm_generic_restore - Generic restore callback for subsystems.
226  * @dev: Device to restore.
227  */
228 int pm_generic_restore(struct device *dev)
229 {
230 	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
231 }
232 EXPORT_SYMBOL_GPL(pm_generic_restore);
233 
234 /**
235  * pm_generic_complete - Generic routine competing a device power transition.
236  * @dev: Device to handle.
237  *
238  * Complete a device power transition during a system-wide power transition.
239  */
240 void pm_generic_complete(struct device *dev)
241 {
242 	struct device_driver *drv = dev->driver;
243 
244 	if (drv && drv->pm && drv->pm->complete)
245 		drv->pm->complete(dev);
246 
247 	/*
248 	 * Let runtime PM try to suspend devices that haven't been in use before
249 	 * going into the system-wide sleep state we're resuming from.
250 	 */
251 	pm_runtime_idle(dev);
252 }
253 #endif /* CONFIG_PM_SLEEP */
254 
255 struct dev_pm_ops generic_subsys_pm_ops = {
256 #ifdef CONFIG_PM_SLEEP
257 	.prepare = pm_generic_prepare,
258 	.suspend = pm_generic_suspend,
259 	.resume = pm_generic_resume,
260 	.freeze = pm_generic_freeze,
261 	.thaw = pm_generic_thaw,
262 	.poweroff = pm_generic_poweroff,
263 	.restore = pm_generic_restore,
264 	.complete = pm_generic_complete,
265 #endif
266 #ifdef CONFIG_PM_RUNTIME
267 	.runtime_suspend = pm_generic_runtime_suspend,
268 	.runtime_resume = pm_generic_runtime_resume,
269 	.runtime_idle = pm_generic_runtime_idle,
270 #endif
271 };
272 EXPORT_SYMBOL_GPL(generic_subsys_pm_ops);
273