Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | Linux Power Management Support This document briefly describes how to use power management with your Linux system and how to add power management support to Linux drivers. APM or ACPI? ------------ If you have a relatively recent x86 mobile, desktop, or server system, odds are it supports either Advanced Power Management (APM) or Advanced Configuration and Power Interface (ACPI). ACPI is the newer of the two technologies and puts power management in the hands of the operating system, allowing for more intelligent power management than is possible with BIOS controlled APM. The best way to determine which, if either, your system supports is to build a kernel with both ACPI and APM enabled (as of 2.3.x ACPI is enabled by default). If a working ACPI implementation is found, the ACPI driver will override and disable APM, otherwise the APM driver will be used. No sorry, you can not have both ACPI and APM enabled and running at once. Some people with broken ACPI or broken APM implementations would like to use both to get a full set of working features, but you simply can not mix and match the two. Only one power management interface can be in control of the machine at once. Think about it.. User-space Daemons ------------------ Both APM and ACPI rely on user-space daemons, apmd and acpid respectively, to be completely functional. Obtain both of these daemons from your Linux distribution or from the Internet (see below) and be sure that they are started sometime in the system boot process. Go ahead and start both. If ACPI or APM is not available on your system the associated daemon will exit gracefully. apmd: http://worldvisions.ca/~apenwarr/apmd/ acpid: http://phobos.fs.tum.de/acpi/ Driver Interface ---------------- If you are writing a new driver or maintaining an old driver, it should include power management support. Without power management support, a single driver may prevent a system with power management capabilities from ever being able to suspend (safely). Overview: 1) Register each instance of a device with "pm_register" 2) Call "pm_access" before accessing the hardware. (this will ensure that the hardware is awake and ready) 3) Your "pm_callback" is called before going into a suspend state (ACPI D1-D3) or after resuming (ACPI D0) from a suspend. 4) Call "pm_dev_idle" when the device is not being used (optional but will improve device idle detection) 5) When unloaded, unregister the device with "pm_unregister" /* * Description: Register a device with the power-management subsystem * * Parameters: * type - device type (PCI device, system device, ...) * id - instance number or unique identifier * cback - request handler callback (suspend, resume, ...) * * Returns: Registered PM device or NULL on error * * Examples: * dev = pm_register(PM_SYS_DEV, PM_SYS_VGA, vga_callback); * * struct pci_dev *pci_dev = pci_find_dev(...); * dev = pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), callback); */ struct pm_dev *pm_register(pm_dev_t type, unsigned long id, pm_callback cback); /* * Description: Unregister a device with the power management subsystem * * Parameters: * dev - PM device previously returned from pm_register */ void pm_unregister(struct pm_dev *dev); /* * Description: Unregister all devices with a matching callback function * * Parameters: * cback - previously registered request callback * * Notes: Provided for easier porting from old APM interface */ void pm_unregister_all(pm_callback cback); /* * Device idle/use detection * * In general, drivers for all devices should call "pm_access" * before accessing the hardware (ie. before reading or modifying * a hardware register). Request or packet-driven drivers should * additionally call "pm_dev_idle" when a device is not being used. * * Examples: * 1) A keyboard driver would call pm_access whenever a key is pressed * 2) A network driver would call pm_access before submitting * a packet for transmit or receive and pm_dev_idle when its * transfer and receive queues are empty. * 3) A VGA driver would call pm_access before it accesses any * of the video controller registers * * Ultimately, the PM policy manager uses the access and idle * information to decide when to suspend individual devices * or when to suspend the entire system */ /* * Description: Update device access time and wake up device, if necessary * * Parameters: * dev - PM device previously returned from pm_register * * Details: If called from an interrupt handler pm_access updates * access time but should never need to wake up the device * (if device is generating interrupts, it should be awake * already) This is important as we can not wake up * devices from an interrupt handler. */ void pm_access(struct pm_dev *dev); /* * Description: Identify device as currently being idle * * Parameters: * dev - PM device previously returned from pm_register * * Details: A call to pm_dev_idle might signal to the policy manager * to put a device to sleep. If a new device request arrives * between the call to pm_dev_idle and the pm_callback * callback, the driver should fail the pm_callback request. */ void pm_dev_idle(struct pm_dev *dev); /* * Power management request callback * * Parameters: * dev - PM device previously returned from pm_register * rqst - request type * data - data, if any, associated with the request * * Returns: 0 if the request is successful * EINVAL if the request is not supported * EBUSY if the device is now busy and can not handle the request * ENOMEM if the device was unable to handle the request due to memory * * Details: The device request callback will be called before the * device/system enters a suspend state (ACPI D1-D3) or * or after the device/system resumes from suspend (ACPI D0). * For PM_SUSPEND, the ACPI D-state being entered is passed * as the "data" argument to the callback. The device * driver should save (PM_SUSPEND) or restore (PM_RESUME) * device context when the request callback is called. * * Once a driver returns 0 (success) from a suspend * request, it should not process any further requests or * access the device hardware until a call to "pm_access" is made. */ typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data); Driver Details -------------- This is just a quick Q&A as a stopgap until a real driver writers' power management guide is available. Q: When is a device suspended? Devices can be suspended based on direct user request (eg. laptop lid closes), system power policy (eg. sleep after 30 minutes of console inactivity), or device power policy (eg. power down device after 5 minutes of inactivity) Q: Must a driver honor a suspend request? No, a driver can return -EBUSY from a suspend request and this will stop the system from suspending. When a suspend request fails, all suspended devices are resumed and the system continues to run. Suspend can be retried at a later time. Q: Can the driver block suspend/resume requests? Yes, a driver can delay its return from a suspend or resume request until the device is ready to handle requests. It is advantageous to return as quickly as possible from a request as suspend/resume are done serially. Q: What context is a suspend/resume initiated from? A suspend or resume is initiated from a kernel thread context. It is safe to block, allocate memory, initiate requests or anything else you can do within the kernel. Q: Will requests continue to arrive after a suspend? Possibly. It is the driver's responsibility to queue(*), fail, or drop any requests that arrive after returning success to a suspend request. It is important that the driver not access its device until after it receives a resume request as the device's bus may no longer be active. (*) If a driver queues requests for processing after resume be aware that the device, network, etc. might be in a different state than at suspend time. It's probably better to drop requests unless the driver is a storage device. Q: Do I have to manage bus-specific power management registers No. It is the responsibility of the bus driver to manage PCI, USB, etc. power management registers. The bus driver or the power management subsystem will also enable any wake-on functionality that the device has. Q: So, really, what do I need to do to support suspend/resume? You need to save any device context that would be lost if the device was powered off and then restore it at resume time. When ACPI is active, there are three levels of device suspend states; D1, D2, and D3. (The suspend state is passed as the "data" argument to the device callback.) With D3, the device is powered off and loses all context, D1 and D2 are shallower power states and require less device context to be saved. To play it safe, just save everything at suspend and restore everything at resume. Q: Where do I store device context for suspend? Anywhere in memory, kmalloc a buffer or store it in the device descriptor. You are guaranteed that the contents of memory will be restored and accessible before resume, even when the system suspends to disk. Q: What do I need to do for ACPI vs. APM vs. etc? Drivers need not be aware of the specific power management technology that is active. They just need to be aware of when the overlying power management system requests that they suspend or resume. Q: What about device dependencies? When a driver registers a device, the power management subsystem uses the information provided to build a tree of device dependencies (eg. USB device X is on USB controller Y which is on PCI bus Z) When power management wants to suspend a device, it first sends a suspend request to its driver, then the bus driver, and so on up to the system bus. Device resumes proceed in the opposite direction. Q: Who do I contact for additional information about enabling power management for my specific driver/device? ACPI4Linux mailing list: acpi@phobos.fs.tum.de System Interface ---------------- If you are providing new power management support to Linux (ie. adding support for something like APM or ACPI), you should communicate with drivers through the existing generic power management interface. /* * Send a request to a single device * * Parameters: * dev - PM device previously returned from pm_register or pm_find * rqst - request type * data - data, if any, associated with the request * * Returns: 0 if the request is successful * See "pm_callback" return for errors * * Details: Forward request to device callback and, if a suspend * or resume request, update the pm_dev "state" field * appropriately */ int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data); /* * Send a request to all devices * * Parameters: * rqst - request type * data - data, if any, associated with the request * * Returns: 0 if the request is successful * See "pm_callback" return for errors * * Details: Walk list of registered devices and call pm_send * for each until complete or an error is encountered. * If an error is encountered for a suspend request, * return all devices to the state they were in before * the suspend request. */ int pm_send_all(pm_request_t rqst, void *data); /* * Find a matching device * * Parameters: * type - device type (PCI device, system device, or 0 to match all devices) * from - previous match or NULL to start from the beginning * * Returns: Matching device or NULL if none found */ struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from); |