Source: http://blog.csdn.net/coolraining/article/details/6678759
Changes in power management methods in the new Linux system device Architecture
Linux-2.6.32 based on
I. Power Management in each data structure of the device model
The device model of Linux is described in combination by multiple struct, such as struct device, struct device_type, struct class,
Struct device_driver, struct bus_type, and so on.
@ Kernel/include/Linux/devices. h contains the struct definitions. Here, only PM-related items are listed. For the rest, see the source code:
Struct device {
...
Struct dev_pm_info power;
...
}
Struct device_type {
...
INT (* uevent) (struct device * Dev, struct kobj_uevent_env * env );
Char * (* devnode) (struct device * Dev, mode_t * mode );
Void (* release) (struct device * Dev );
Const struct dev_pm_ops * PM;
};
Struct class {
...
Void (* class_release) (struct class * class );
Void (* dev_release) (struct device * Dev );
INT (* suspend) (struct device * Dev, pm_message_t State );
INT (* resume) (struct device * Dev );
Const struct dev_pm_ops * PM;
...
};
Struct device_driver {
...
INT (* probe) (struct device * Dev );
INT (* remove) (struct device * Dev );
Void (* shutdown) (struct device * Dev );
INT (* suspend) (struct device * Dev, pm_message_t State );
INT (* resume) (struct device * Dev );
Const struct dev_pm_ops * PM;
...
};
Struct bus_type {
...
INT (* match) (struct device * Dev, struct device_driver * DRV );
INT (* uevent) (struct device * Dev, struct kobj_uevent_env * env );
INT (* probe) (struct device * Dev );
INT (* remove) (struct device * Dev );
Void (* shutdown) (struct device * Dev );
INT (* suspend) (struct device * Dev, pm_message_t State );
INT (* resume) (struct device * Dev );
Const struct dev_pm_ops * PM;
...
};
The two structures related to power management are struct dev_pm_info and struct dev_pm_ops, which are defined in the file
@ Kernel/include/Linux/PM. h
Struct dev_pm_info {
Pm_message_t power_state;
Unsigned int can_wakeup: 1;
Unsigned int should_wakeup: 1;
Enum dpm_state status;/* owned by the PM core-indicates the current PM status of the device */
# Ifdef config_pm_sleep
Struct list_head entry;/* link to the connector in the dpm_list global linked list */
# Endif
# Ifdef config_pm_runtime // UNDEF
Struct timer_list suspend_timer;
Unsigned long timer_expires;
Struct work_struct work;
Wait_queue_head_t wait_queue;
Spinlock_t lock;
Atomic_t usage_count;
Atomic_t child_count;
Unsigned int disable_depth: 3;
Unsigned int ignore_children: 1;
Unsigned int idle_notification: 1;
Unsigned int request_pending: 1;
Unsigned int deferred_resume: 1;
Enum rpm_request request;
Enum rpm_status runtime_status;
Int runtime_error;
# Endif
};
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 );
};
2. dev_pm_info struct in Device
The power item in the device struct is used to include the device into the power management scope and record some information about power management.
Call the device_add () function when registering a device to add a power interface to sysfs and register it in the power management system. The code snippet is as follows:
...
Error = dpm_sysfs_add (Dev); @ kernel/Drivers/base/power/sysfs. c
If (error)
Goto dpmerror;
Device_pm_add (Dev); @ kernel/Drivers/base/power/Main. c
...
The dpm_sysfs_add () function is used to add the power interface file of the corresponding device to the sysfs file system. For example, when registering mt6516_tpd paltform device, the following directories and files will appear in sysfs:
# Pwd
/Sys/devices/platform/mt6516-tpd
# Cd mt6516-tpd
# Ls-l
-RW-r -- root Root 4096 2010-01-02 uevent
-R -- root Root 4096 2010-01-02 06:39 modalias
Lrwxrwxrwx Root 2010-01-02 06:39 subsystem->.../../bus/Platform
Drwxr-XR-x Root 2010-01-02 power
Lrwxrwxrwx Root 2010-01-02 driver->.../../bus/platform/Drivers/mt6516-tpd
# Cd power
# Ls-l
-RW-r -- root Root 4096 2010-01-02 Wakeup
Source code snippet:
Static device_attr (wakeup, 0644, wake_show, wake_store );
Static struct attribute * power_attrs [] = {
& Dev_attr_wakeup.attr,
Null,
};
Static struct attribute_group pm_attr_group = {
. Name = "power", // if the name field of the attribute_group struct is not null, an attribute directory named
. Attrs = power_attrs,
};
Int dpm_sysfs_add (struct device * Dev)
{
Return sysfs_create_group (& Dev-> kobj, & pm_attr_group); // create a value in the directory corresponding to the current device's kobject struct
}
The device_pm_add () function inserts the device into the core list dpm_list of power management for unified management.
It is worth mentioning that in the device_initialize () function, device_pm_init () is called to initialize the power field of the device struct:
Dev-> power. Status = dpm_on;
Void device_pm_add (struct device * Dev)
{
...
Mutex_lock (& dpm_list_ctx );
If (Dev-> parent ){
If (Dev-> parent-> power. Status> = dpm_suspending)
// If a device is in the status after dpm_suspending, you cannot register a sub-device with this device as its parent device.
Dev_warn (Dev, "parent % s shocould not be sleeping/N", dev_name (Dev-> parent ));
} Else if (transition_started) {// The transition_started global variable contains a device that is not allowed to be registered during PM transition
/*
* We refuse to register parentless devices while a PM
* Transition is in progress in order to avoid leaving them
* Unhandled down the road
*/
Dev_warn (Dev, "parentless device registered during a PM transaction/N ");
}
List_add_tail (& Dev-> power. Entry, & dpm_list); // connect the device struct to the dpm_list using the power. Entry necklace.
Mutex_unlock (& dpm_list_ctx );
}
Void device_pm_remove (struct device * Dev)
{
...
Mutex_lock (& dpm_list_ctx );
List_del_init (& Dev-> power. Entry );
Mutex_unlock (& dpm_list_ctx );
Pm_runtime_remove (Dev );
}
Example:
The well-known platform bus is also registered into the system as a device in the system. The position in the sysfs file system is:
/Sys/devices/platform. Use the device_register (& platform_bus) function to register and call the device_add () function,
After registering OK, the directory/sys/devices/platform/power will also appear. It will also be added to dpm_list.
The I2C controller peripheral represents a device registered on the platform bus, that is, its parent device is platform.
Finally, in platform_device_add (), the device_add () function is called to add the device/
A power directory appears in the mt6516-i2c.1/mt6516-i2c.2, and the three platform devices depend on
The platform_device.dev.power.entry connector is linked to the power management core linked list dpm_list.
/Sys/devices/platform/mt6516-i2c.2/power
Each I2C controller registers at least one adapter in the system. This structure is provided indirectly to the driver of the I2C device to avoid using the I2C controller structure directly. This adapter does not have a corresponding driver. In the intricate I2C architecture, it is connected to the I2C controller's struct and driver, i2c_client, which is connected to the I2C device, and the driver that features it. Adapter. Dev. parent is the device corresponding to the I2C controller, so the device kobject named i2c-0/1/2 appears, but the bus and device_type of the device are:
ADAP-> Dev. Bus = & i2c_bus_type;
ADAP-> Dev. type = & i2c_adapter_type;
Then call the function device_register (& ADAP-> Dev); to register this device, so the power directory appears under the corresponding i2c-0/1/2 directory.
/Sys/devices/platform/mt6516-i2c.2/i2c-2/power
The I2C device registers the device to the system through automatic detection or pre-static description. in any way, the i2c_new_device () function is called ()
Struct i2c_client * client;
Client-> Dev. Parent = & client-> adapter-> dev;
Client-> Dev. Bus = & i2c_bus_type;
Client-> Dev. type = & i2c_client_type;
Dev_set_name (& client-> Dev, "% d-% 04x", i2c_adapter_id (ADAP ),
Client-> ADDR );
Status = device_register (& client-> Dev );
We can see what the name is, for example, 2-00aa.
# Ls-L/sys/devices/platform/mt6516-i2c.2/i2c-2/2-00aa
-RW-r -- root Root 4096 2010-01-02 uevent
-R -- root Root 4096 2010-01-02 06:38 name
-R -- root Root 4096 2010-01-02 06:38 modalias
Lrwxrwxrwx Root 2010-01-02 06:38 subsystem->.../../bus/I2C
Drwxr-XR-x Root 2010-01-02 power
Lrwxrwxrwx Root 2010-01-02 driver->.../bus/I2C/Drivers/mt6516-tpd
3. Structure of the dev_pm_ops method in bus_type, device_driver, device_type, and Class
In the new Linux kernel, the subsystem data structure is no longer available, and its functions are replaced by kset.
Initialize the global variable bus_kset: kernel_init () --> do_basic_setup () --> driver_init () --> buses_init ()
Bus_kset = kset_create_and_add ("bus", & bus_uevent_ops, null );
1. Bus Type Structure: bus_type. Take platform and I2C bus as an example:
@ Kernel/Drivers/base/platform. c
Static const struct dev_pm_ops platform_dev_pm_ops = {
. Prepare = platform_pm_prepare ,//
. Complete = platform_pm_complete ,//
. Suspend = platform_pm_suspend ,//
. Resume = platform_pm_resume ,//
. Freeze = platform_pm_freeze,
. Thaw = platform_pm_thaw,
. Poweroff = platform_pm_poweroff ,//
. Restore = platform_pm_restore,
. Suspend_noirq = platform_pm_suspend_noirq,
. Resume_noirq = platform_pm_resume_noirq,
. Freeze_noirq = platform_pm_freeze_noirq,
. Thaw_noirq = platform_pm_thaw_noirq,
. Poweroff_noirq = platform_pm_poweroff_noirq,
. Restore_noirq = platform_pm_restore_noirq,
. Runtime_suspend = platform_pm_runtime_suspend,
. Runtime_resume = platform_pm_runtime_resume,
. Runtime_idle = platform_pm_runtime_idle,
};
Struct bus_type platform_bus_type = {
. Name = "Platform ",
. Dev_attrs = platform_dev_attrs,
. Match = platform_match,
. Uevent = platform_uevent,
. PM = & platform_dev_pm_ops,
};
From the above dev_pm_ops struct, the most commonly used function pointer shows how the power management of bus_type is implemented.
Static int platform_pm_prepare (struct device * Dev)
{
Struct device_driver * DRV = Dev-> driver;
Int ret = 0;
If (DRV & DRV-> PM-> prepare)
Ret = DRV-> PM-> prepare (Dev );
Return ret;
}
Static void platform_pm_complete (struct device * Dev)
{
Struct device_driver * DRV = Dev-> driver;
If (DRV & DRV-> PM-> complete)
DRV-> PM-> complete (Dev );
}
We can see that both functions use the corresponding function pointer in the dev_pm_ops Function Method struct of device_driver.
//////////////////////////////////////// ////
Static int platform_legacy_suspend (struct device * Dev, pm_message_t mesg)
{
Struct platform_driver * pdrv = to_platform_driver (Dev-> driver );
Struct platform_device * pdev = to_platform_device (Dev );
Int ret = 0;
If (Dev-> driver & pdrv-> suspend)
Ret = pdrv-> suspend (pdev, mesg );
Return ret;
}
Static int platform_legacy_resume (struct device * Dev)
{
Struct platform_driver * pdrv = to_platform_driver (Dev-> driver );
Struct platform_device * pdev = to_platform_device (Dev );
Int ret = 0;
If (Dev-> driver & pdrv-> resume)
Ret = pdrv-> resume (pdev );
Return ret;
}
//////////////////////////////////////// ////
Static int platform_pm_suspend (struct device * Dev)
{
Struct device_driver * DRV = Dev-> driver;
Int ret = 0;
If (! DRV)
Return 0;
If (DRV-> PM ){
If (DRV-> PM-> suspend)
Ret = DRV-> PM-> suspend (Dev );
} Else {
Ret = platform_legacy_suspend (Dev, pmsg_suspend );
}
Return ret;
}
Static int platform_pm_resume (struct device * Dev)
{
Struct device_driver * DRV = Dev-> driver;
Int ret = 0;
If (! DRV)
Return 0;
If (DRV-> PM ){
If (DRV-> PM-> resume)
Ret = DRV-> PM-> resume (Dev );
} Else {
Ret = platform_legacy_resume (Dev );
}
Return ret;
}
The suspend and resume functions call the corresponding function pointer in the dev_pm_ops method struct of the device_driver structure (the device_driver.pm item is initialized). Otherwise, the old method platform_legacy_suspend (Dev, pmsg_suspend) is used) and platform_legacy_resume (Dev ). We can see from the source code of these two functions. Generally, in the platform driver definition of platform device, pdrv is implemented. suspend and pdrv. the Resume function does not implement pdrv. driver. suspend and pdrv. driver. the Resume function. The remaining three functions can be seen in the platform_driver_register () function:
Int platform_driver_register (struct platform_driver * DRV)
{
DRV-> driver. Bus = & platform_bus_type;
If (DRV-> probe)
DRV-> driver. Probe = platform_drv_probe;
If (DRV-> remove)
DRV-> driver. Remove = platform_drv_remove;
If (DRV-> shutdown)
DRV-> driver. Shutdown = platform_drv_shutdown;
Return driver_register (& DRV-> driver );
}
I2C bus registration does not use the new power management method: dev_pm_ops, still using the old method:
@ Kernel/Drivers/I2C/i2c-core.c
Struct bus_type i2c_bus_type = {
. Name = "I2C ",
. Match = i2c_device_match,
. Probe = i2c_device_probe,
. Remove = i2c_device_remove,
. Shutdown = i2c_device_shutdown,
. Suspend = i2c_device_suspend,
. Resume = i2c_device_resume,
};
Static int i2c_device_suspend (struct device * Dev, pm_message_t mesg)
{
Struct i2c_client * client = i2c_verify_client (Dev );
Struct i2c_driver * driver;
If (! Client |! Dev-> driver)
Return 0;
Driver = to_i2c_driver (Dev-> driver );
If (! Driver-> suspend)
Return 0;
Return driver-> suspend (client, mesg );
}
Static int i2c_device_resume (struct device * Dev)
{
Struct i2c_client * client = i2c_verify_client (Dev );
Struct i2c_driver * driver;
If (! Client |! Dev-> driver)
Return 0;
Driver = to_i2c_driver (Dev-> driver );
If (! Driver-> resume)
Return 0;
Return driver-> resume (client );
}
// The suspend and resume functions of the i2c_driver struct are actually called.
2. Currently, the device_type struct does not find any module that uses the new dev_pm_ops power management method.
3. The class struct cannot be found where the dev_pm_ops method struct is used.
4. device_driver
Struct device_driver {
Const char * Name;
Struct bus_type * bus;
...
INT (* probe) (struct device * Dev );
INT (* remove) (struct device * Dev );
Void (* shutdown) (struct device * Dev );
INT (* suspend) (struct device * Dev, pm_message_t State );
INT (* resume) (struct device * Dev );
Const struct attribute_group ** groups;
Const struct dev_pm_ops * PM;
Struct driver_private * P;
};
Struct i2c_driver {
...
/* Driver Model interfaces that don't relate to enumeration */
Void (* shutdown) (struct i2c_client *);
INT (* suspend) (struct i2c_client *, pm_message_t mesg );
INT (* resume) (struct i2c_client *);
...
Struct device_driver driver;
Const struct i2c_device_id * id_table;
/* Device Detection callback for automatic device creation */
INT (* detect) (struct i2c_client *, int kind, struct i2c_board_info *);
Const struct i2c_client_address_data * address_data;
Struct list_head clients;
};
Generally, the suspend and resume functions of the platform driver and i2c_driver struct are implemented, and the new power management mode is not used.