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 * @bool: Whether or not this is the "noirq" stage. 98 * 99 * If the device has not been suspended at run time, execute the 100 * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and 101 * return its error code. Otherwise, return zero. 102 */ 103 static int __pm_generic_call(struct device *dev, int event, bool noirq) 104 { 105 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 106 int (*callback)(struct device *); 107 108 if (!pm || pm_runtime_suspended(dev)) 109 return 0; 110 111 switch (event) { 112 case PM_EVENT_SUSPEND: 113 callback = noirq ? pm->suspend_noirq : pm->suspend; 114 break; 115 case PM_EVENT_FREEZE: 116 callback = noirq ? pm->freeze_noirq : pm->freeze; 117 break; 118 case PM_EVENT_HIBERNATE: 119 callback = noirq ? pm->poweroff_noirq : pm->poweroff; 120 break; 121 case PM_EVENT_THAW: 122 callback = noirq ? pm->thaw_noirq : pm->thaw; 123 break; 124 default: 125 callback = NULL; 126 break; 127 } 128 129 return callback ? callback(dev) : 0; 130 } 131 132 /** 133 * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems. 134 * @dev: Device to suspend. 135 */ 136 int pm_generic_suspend_noirq(struct device *dev) 137 { 138 return __pm_generic_call(dev, PM_EVENT_SUSPEND, true); 139 } 140 EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); 141 142 /** 143 * pm_generic_suspend - Generic suspend callback for subsystems. 144 * @dev: Device to suspend. 145 */ 146 int pm_generic_suspend(struct device *dev) 147 { 148 return __pm_generic_call(dev, PM_EVENT_SUSPEND, false); 149 } 150 EXPORT_SYMBOL_GPL(pm_generic_suspend); 151 152 /** 153 * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems. 154 * @dev: Device to freeze. 155 */ 156 int pm_generic_freeze_noirq(struct device *dev) 157 { 158 return __pm_generic_call(dev, PM_EVENT_FREEZE, true); 159 } 160 EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); 161 162 /** 163 * pm_generic_freeze - Generic freeze callback for subsystems. 164 * @dev: Device to freeze. 165 */ 166 int pm_generic_freeze(struct device *dev) 167 { 168 return __pm_generic_call(dev, PM_EVENT_FREEZE, false); 169 } 170 EXPORT_SYMBOL_GPL(pm_generic_freeze); 171 172 /** 173 * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems. 174 * @dev: Device to handle. 175 */ 176 int pm_generic_poweroff_noirq(struct device *dev) 177 { 178 return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true); 179 } 180 EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); 181 182 /** 183 * pm_generic_poweroff - Generic poweroff callback for subsystems. 184 * @dev: Device to handle. 185 */ 186 int pm_generic_poweroff(struct device *dev) 187 { 188 return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false); 189 } 190 EXPORT_SYMBOL_GPL(pm_generic_poweroff); 191 192 /** 193 * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems. 194 * @dev: Device to thaw. 195 */ 196 int pm_generic_thaw_noirq(struct device *dev) 197 { 198 return __pm_generic_call(dev, PM_EVENT_THAW, true); 199 } 200 EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); 201 202 /** 203 * pm_generic_thaw - Generic thaw callback for subsystems. 204 * @dev: Device to thaw. 205 */ 206 int pm_generic_thaw(struct device *dev) 207 { 208 return __pm_generic_call(dev, PM_EVENT_THAW, false); 209 } 210 EXPORT_SYMBOL_GPL(pm_generic_thaw); 211 212 /** 213 * __pm_generic_resume - Generic resume/restore callback for subsystems. 214 * @dev: Device to handle. 215 * @event: PM transition of the system under way. 216 * @bool: Whether or not this is the "noirq" stage. 217 * 218 * Execute the resume/resotre callback provided by the @dev's driver, if 219 * defined. If it returns 0, change the device's runtime PM status to 'active'. 220 * Return the callback's error code. 221 */ 222 static int __pm_generic_resume(struct device *dev, int event, bool noirq) 223 { 224 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 225 int (*callback)(struct device *); 226 int ret; 227 228 if (!pm) 229 return 0; 230 231 switch (event) { 232 case PM_EVENT_RESUME: 233 callback = noirq ? pm->resume_noirq : pm->resume; 234 break; 235 case PM_EVENT_RESTORE: 236 callback = noirq ? pm->restore_noirq : pm->restore; 237 break; 238 default: 239 callback = NULL; 240 break; 241 } 242 243 if (!callback) 244 return 0; 245 246 ret = callback(dev); 247 if (!ret && !noirq && pm_runtime_enabled(dev)) { 248 pm_runtime_disable(dev); 249 pm_runtime_set_active(dev); 250 pm_runtime_enable(dev); 251 } 252 253 return ret; 254 } 255 256 /** 257 * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems. 258 * @dev: Device to resume. 259 */ 260 int pm_generic_resume_noirq(struct device *dev) 261 { 262 return __pm_generic_resume(dev, PM_EVENT_RESUME, true); 263 } 264 EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); 265 266 /** 267 * pm_generic_resume - Generic resume callback for subsystems. 268 * @dev: Device to resume. 269 */ 270 int pm_generic_resume(struct device *dev) 271 { 272 return __pm_generic_resume(dev, PM_EVENT_RESUME, false); 273 } 274 EXPORT_SYMBOL_GPL(pm_generic_resume); 275 276 /** 277 * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems. 278 * @dev: Device to restore. 279 */ 280 int pm_generic_restore_noirq(struct device *dev) 281 { 282 return __pm_generic_resume(dev, PM_EVENT_RESTORE, true); 283 } 284 EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); 285 286 /** 287 * pm_generic_restore - Generic restore callback for subsystems. 288 * @dev: Device to restore. 289 */ 290 int pm_generic_restore(struct device *dev) 291 { 292 return __pm_generic_resume(dev, PM_EVENT_RESTORE, false); 293 } 294 EXPORT_SYMBOL_GPL(pm_generic_restore); 295 296 /** 297 * pm_generic_complete - Generic routine competing a device power transition. 298 * @dev: Device to handle. 299 * 300 * Complete a device power transition during a system-wide power transition. 301 */ 302 void pm_generic_complete(struct device *dev) 303 { 304 struct device_driver *drv = dev->driver; 305 306 if (drv && drv->pm && drv->pm->complete) 307 drv->pm->complete(dev); 308 309 /* 310 * Let runtime PM try to suspend devices that haven't been in use before 311 * going into the system-wide sleep state we're resuming from. 312 */ 313 pm_runtime_idle(dev); 314 } 315 #endif /* CONFIG_PM_SLEEP */ 316 317 struct dev_pm_ops generic_subsys_pm_ops = { 318 #ifdef CONFIG_PM_SLEEP 319 .prepare = pm_generic_prepare, 320 .suspend = pm_generic_suspend, 321 .suspend_noirq = pm_generic_suspend_noirq, 322 .resume = pm_generic_resume, 323 .resume_noirq = pm_generic_resume_noirq, 324 .freeze = pm_generic_freeze, 325 .freeze_noirq = pm_generic_freeze_noirq, 326 .thaw = pm_generic_thaw, 327 .thaw_noirq = pm_generic_thaw_noirq, 328 .poweroff = pm_generic_poweroff, 329 .poweroff_noirq = pm_generic_poweroff_noirq, 330 .restore = pm_generic_restore, 331 .restore_noirq = pm_generic_restore_noirq, 332 .complete = pm_generic_complete, 333 #endif 334 #ifdef CONFIG_PM_RUNTIME 335 .runtime_suspend = pm_generic_runtime_suspend, 336 .runtime_resume = pm_generic_runtime_resume, 337 .runtime_idle = pm_generic_runtime_idle, 338 #endif 339 }; 340 EXPORT_SYMBOL_GPL(generic_subsys_pm_ops); 341