power management in linux

来源:互联网 发布:足球大师2捏脸数据库 编辑:程序博客网 时间:2024/06/17 22:52

http://szakul.ovh.org/wiki/doku.php?id=power_management_in_linux&DokuWiki=3c1ccc581e0a44b8587116430902a60d

power management in linux

At the time of writing this article, there are two models for device power management. Drivers can use one or both of these models to put devices in low-power states:

-system sleep model:

Drivers can enter low power states as part of entering system-wide low-power states like „suspend-to-ram”, or „hibernate” (suspend-to-disk).

-runtime power management model:

Drivers can enter into low power states while the system is running independently of other power management activity.


system sleep model

It is quite easy to implement suspend/resume mechanism. To support it, the device driver has to register to the Linux Device Model core (using driver_register, platform_driver_register, pci_register_driver etc). Then it also needs to define .suspend, .resume methods which are defined in the *_driver structures (device_driver, platform_driver, usb_driver, pci_driver etc.). Or it has to define the .suspend .resume methods which are defined in pm field. The pm is an object of type dev_pm_ops. The full definition is as follows:

struct dev_pm_ops {  int (*prepare)(struct device *dev);  void (*complete)(struct device *dev);  int (*suspend)(struct device *dev);  int (*resume)(struct device *dev);  int (*freeze)(struct device *dev);  int (*thaw)(struct device *dev);  int (*poweroff)(struct device *dev);  int (*restore)(struct device *dev);  int (*suspend_noirq)(struct device *dev);  int (*resume_noirq)(struct device *dev);  int (*freeze_noirq)(struct device *dev);  int (*thaw_noirq)(struct device *dev);  int (*poweroff_noirq)(struct device *dev);  int (*restore_noirq)(struct device *dev);  int (*runtime_suspend)(struct device *dev);  int (*runtime_resume)(struct device *dev);  int (*runtime_idle)(struct device *dev);};

This structure is defined in include/linux/pm.h file and there you can find the description of them. You can see that there are also callbacks supporting runtime power management but we will talk about it later.

As an example I will show how to implement resume/suspend mechanism for platform device.

There are two ways for defining power management callbacks for a platform device drivers. The first one, which is recommended, is based on using the dev_pm_ops structure and the 'legacy' one in which the .suspend () and resume () callbacks from struct platform_driver are used. The 'legacy' approach doesn't allow to define runtime power management callbacks. In the first way, driver must define a struct dev_pm_ops object containing pointers to power management (PM) callbacks that will be executed by the platform subsystem's PM routines. A pointer to the driver's struct dev_pm_ops object has to be assigned to the driver.pm field in its struct platform_driver object. Then, the „legacy” PM callbacks in struct platform_driver are ignored (even if they are not NULL).

static struct platform_device *pDevice;static int drv_pm_suspend (struct device *dev){  return (0);}static int drv_pm_resume (struct device *dev){  return (0);}struct dev_pm_ops ops_pm = {  .suspend = dev_pm_suspend,  .resume  = dev_pm_resume,};#define OPS_PM (&ops_pm)static struct platform_driver driver ={  .driver =   {    .name  = "my_driver",    .owner = THIS_MODULE,    .pm    = OPS_PM,  },};static int __init initModule (void){  int rc;    rc = platform_driver_register (&driver);  if (rc)  {    printk (KERN_INFO "hello: Error platform driver register !!!!!!\n");    return (rc);  }  pDevice = platform_device_alloc ("my_driver", -1);  platform_device_add (pDevice);    return (0);}static void __exit cleanupModule (void){  platform_device_unregister (pDevice);  platform_driver_unregister (&driver);  }

runtime power management model

Enables saving energy when the devices are 'idle' by means of changing device low-power state. You have to define 3 runtime PM callbacks defined in struct dev_pm_ops: .runtime_suspend, .runtime_resume, .runtime_idle.

As default, the runtime PM is disabled for all devices and it means that all related functions will return -EAGAIN. To enable runtime PM call function pm_runtime_enable (). The initial runtime PM status of all devices is 'suspended' and it not need reflect the actual physical state of device. Therefore, its status should be changed to 'active' using function pm_runtime_set_active (), before pm_runtime_enable () is called. You can decide if device should enter suspend state or be active by means of incrementing/decrementing the pm 'counter' using functions pm_runtime_put_*/pm_runtime_get_*. Devices with referenced held can not be suspended (similar to 'wakelocks' mechanism implemented in Android). Devices for which the counter is equal to 0 go to suspend state.

There are many helper functions for runtime PM whose description can be found in Documentation/power/pm_runtime.txt.

Below you can find an simple example of runtime PM implementation for platform device.

static struct platform_device *pDevice;static int drv_pm_runtime_suspend (struct device *dev){  return (0);}static int drv_pm__runtime_resume (struct device *dev){  return (0);}static int drv_pm__runtime_idle (struct device *dev){  return (0);}struct dev_pm_ops ops_pm = {  .runtime_suspend = drv_pm_runtime_suspend,  .runtime_resume  = drv_pm__runtime_resume,  .runtime_idle    = drv_pm__runtime_idle,};#define OPS_PM (&ops_pm)static int dev_probe (struct platform_device *pdev){  pm_runtime_set_active (&pdev->dev);  pm_runtime_enable (&pdev->dev);  return (0);}static int dev_remove (struct platform_device *pdev){  pm_runtime_disable (&pdev->dev);  pm_runtime_set_suspended (&pdev->dev);    return (0);}static struct platform_driver driver ={  .driver =   {    .name  = "my_driver",    .owner = THIS_MODULE,    .pm    = OPS_PM,  },  .probe  = dev_probe,  .remove = dev_remove,};void doSomething (struct device *dev){  //resume device immediately  pm_runtime_get_sync (dev);  //...action on active device  pm_runtime_put_sync (dev);  //if all done, suspend device immediatelly}
static int __init initModule (void){  int rc;    rc = platform_driver_register (&driver);  if (rc)  {    printk (KERN_INFO "hello: Error platform driver register !!!!!!\n");    return (rc);  }  pDevice = platform_device_alloc ("my_driver", -1);  platform_device_add (pDevice);    return (0);}static void __exit cleanupModule (void){  platform_device_unregister (pDevice);  platform_driver_unregister (&driver);  }

0 0
原创粉丝点击