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