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 */ 21int 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} 34EXPORT_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 -EINVAL. 43 */ 44int 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) : -EINVAL; 50 51 return ret; 52} 53EXPORT_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 -EINVAL. 62 */ 63int 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) : -EINVAL; 69 70 return ret; 71} 72EXPORT_SYMBOL_GPL(pm_generic_runtime_resume); 73#endif /* CONFIG_PM_RUNTIME */ 74 75#ifdef CONFIG_PM_SLEEP 76/** 77 * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback. 78 * @dev: Device to handle. 79 * @event: PM transition of the system under way. 80 * 81 * If the device has not been suspended at run time, execute the 82 * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and 83 * return its error code. Otherwise, return zero. 84 */ 85static int __pm_generic_call(struct device *dev, int event) 86{ 87 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 88 int (*callback)(struct device *); 89 90 if (!pm || pm_runtime_suspended(dev)) 91 return 0; 92 93 switch (event) { 94 case PM_EVENT_SUSPEND: 95 callback = pm->suspend; 96 break; 97 case PM_EVENT_FREEZE: 98 callback = pm->freeze; 99 break; 100 case PM_EVENT_HIBERNATE: 101 callback = pm->poweroff; 102 break; 103 case PM_EVENT_THAW: 104 callback = pm->thaw; 105 break; 106 default: 107 callback = NULL; 108 break; 109 } 110 111 return callback ? callback(dev) : 0; 112} 113 114/** 115 * pm_generic_suspend - Generic suspend callback for subsystems. 116 * @dev: Device to suspend. 117 */ 118int pm_generic_suspend(struct device *dev) 119{ 120 return __pm_generic_call(dev, PM_EVENT_SUSPEND); 121} 122EXPORT_SYMBOL_GPL(pm_generic_suspend); 123 124/** 125 * pm_generic_freeze - Generic freeze callback for subsystems. 126 * @dev: Device to freeze. 127 */ 128int pm_generic_freeze(struct device *dev) 129{ 130 return __pm_generic_call(dev, PM_EVENT_FREEZE); 131} 132EXPORT_SYMBOL_GPL(pm_generic_freeze); 133 134/** 135 * pm_generic_poweroff - Generic poweroff callback for subsystems. 136 * @dev: Device to handle. 137 */ 138int pm_generic_poweroff(struct device *dev) 139{ 140 return __pm_generic_call(dev, PM_EVENT_HIBERNATE); 141} 142EXPORT_SYMBOL_GPL(pm_generic_poweroff); 143 144/** 145 * pm_generic_thaw - Generic thaw callback for subsystems. 146 * @dev: Device to thaw. 147 */ 148int pm_generic_thaw(struct device *dev) 149{ 150 return __pm_generic_call(dev, PM_EVENT_THAW); 151} 152EXPORT_SYMBOL_GPL(pm_generic_thaw); 153 154/** 155 * __pm_generic_resume - Generic resume/restore callback for subsystems. 156 * @dev: Device to handle. 157 * @event: PM transition of the system under way. 158 * 159 * Execute the resume/resotre callback provided by the @dev's driver, if 160 * defined. If it returns 0, change the device's runtime PM status to 'active'. 161 * Return the callback's error code. 162 */ 163static int __pm_generic_resume(struct device *dev, int event) 164{ 165 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 166 int (*callback)(struct device *); 167 int ret; 168 169 if (!pm) 170 return 0; 171 172 switch (event) { 173 case PM_EVENT_RESUME: 174 callback = pm->resume; 175 break; 176 case PM_EVENT_RESTORE: 177 callback = pm->restore; 178 break; 179 default: 180 callback = NULL; 181 break; 182 } 183 184 if (!callback) 185 return 0; 186 187 ret = callback(dev); 188 if (!ret) { 189 pm_runtime_disable(dev); 190 pm_runtime_set_active(dev); 191 pm_runtime_enable(dev); 192 } 193 194 return ret; 195} 196 197/** 198 * pm_generic_resume - Generic resume callback for subsystems. 199 * @dev: Device to resume. 200 */ 201int pm_generic_resume(struct device *dev) 202{ 203 return __pm_generic_resume(dev, PM_EVENT_RESUME); 204} 205EXPORT_SYMBOL_GPL(pm_generic_resume); 206 207/** 208 * pm_generic_restore - Generic restore callback for subsystems. 209 * @dev: Device to restore. 210 */ 211int pm_generic_restore(struct device *dev) 212{ 213 return __pm_generic_resume(dev, PM_EVENT_RESTORE); 214} 215EXPORT_SYMBOL_GPL(pm_generic_restore); 216#endif /* CONFIG_PM_SLEEP */ 217 218struct dev_pm_ops generic_subsys_pm_ops = { 219#ifdef CONFIG_PM_SLEEP 220 .suspend = pm_generic_suspend, 221 .resume = pm_generic_resume, 222 .freeze = pm_generic_freeze, 223 .thaw = pm_generic_thaw, 224 .poweroff = pm_generic_poweroff, 225 .restore = pm_generic_restore, 226#endif 227#ifdef CONFIG_PM_RUNTIME 228 .runtime_suspend = pm_generic_runtime_suspend, 229 .runtime_resume = pm_generic_runtime_resume, 230 .runtime_idle = pm_generic_runtime_idle, 231#endif 232}; 233EXPORT_SYMBOL_GPL(generic_subsys_pm_ops); 234