http://blog.csdn.net/lizhiguo0532/article/details/6453567
新版linux系統設備架構中關於電源管理方式的變更
based on linux-2.6.32
一、設備模型各數據結構中電源管理的部分
linux的設備模型通過諸多結構體來聯合描述,如struct device,struct device_type,struct class,
struct device_driver,struct bus_type等。
@kernel/include/linux/devices.h中有這幾中結構體的定義,這裏只列出和PM有關的項,其餘查看源碼:
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;
...
};
以上可以看出和電源管理相關的兩個結構體是struct dev_pm_info和struct dev_pm_ops,他們定義於文件
@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 - 表示該設備當前的PM狀態*/
#ifdef CONFIG_PM_SLEEP
struct list_head entry; /* 鏈接到dpm_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);
};
二、device中的dev_pm_info結構體
device結構體中的power項用來將該設備納入電源管理的範圍,記錄電源管理的一些信息。
在註冊設備的時候調用函數device_add()來向sysfs系統添加power接口和註冊進電源管理系統,代碼片段如下:
...
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
...
其中dpm_sysfs_add()函數用來向sysfs文件系統中添加相應設備的power接口文件,如註冊mt6516_tpd paltform device的時候,會在sysfs中出現如下目錄和文件:
#pwd
/sys/devices/platform/mt6516-tpd
#cd mt6516-tpd
#ls -l
-rw-r--r-- root root 4096 2010-01-02 06:35 uevent
-r--r--r-- root root 4096 2010-01-02 06:39 modalias
lrwxrwxrwx root root 2010-01-02 06:39 subsystem -> ../../../bus/platform
drwxr-xr-x root root 2010-01-02 06:35 power
lrwxrwxrwx root root 2010-01-02 06:39 driver -> ../../../bus/platform/drivers/mt6516-tpd
#cd power
#ls -l
-rw-r--r-- root root 4096 2010-01-02 06:39 wakeup
源碼片段:
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", // attribute_group結構體的name域不爲NULL的話,都會已name建立一個屬性目錄的
.attrs = power_attrs,
};
int dpm_sysfs_add(struct device * dev)
{
return sysfs_create_group(&dev->kobj, &pm_attr_group); //在當前device的kobject結構體對應的目錄下建立
}
其中的device_pm_add()函數會將該設備插入到電源管理的核心鏈表dpm_list中統一管理。
值得一提的是,在函數device_initialize()會調用函數device_pm_init()來初始化該device結構體的power域:
dev->power.status = DPM_ON;
void device_pm_add(struct device *dev)
{
...
mutex_lock(&dpm_list_mtx);
if (dev->parent) {
if (dev->parent->power.status >= DPM_SUSPENDING)
// 如果某設備處於DPM_SUSPENDING極其之後的狀態,此時不允許以該設備爲父設備註冊子設備
dev_warn(dev, "parent %s should not be sleeping/n", dev_name(dev->parent));
} else if (transition_started) { // transition_started全局變量包含在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); // 將device結構體通過power.entry項鍊接進dpm_list
mutex_unlock(&dpm_list_mtx);
}
void device_pm_remove(struct device *dev)
{
...
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
pm_runtime_remove(dev);
}
舉例說明:
我們熟知的platform bus在系統中也是作爲一種設備註冊進了系統,在sysfs文件系統中的位置是:
/sys/devices/platform。使用函數device_register(&platform_bus)進行註冊,調用device_add()函數,
註冊ok之後,也會出現目錄/sys/devices/platform/power。最後也會將其添加進dpm_list中。
i2c控制器外設代表的設備是註冊在platform總線上的,也就是說它的父設備是platform。
最終在platform_device_add()中會調用函數device_add()函數來添加設備,最終也會在mt6516-i2c.0/
mt6516-i2c.1/mt6516-i2c.2中出現一個power目錄,同時這3個platform設備會依靠
platform_device.dev.power.entry連接件鏈接到電源管理核心鏈表dpm_list中。
/sys/devices/platform/mt6516-i2c.2/power
每一個i2c控制器都會在系統中至少註冊成一個適配器(adapter),該結構體將會間接提供給i2c設備的驅動來使用,以避免直接使用i2c控制器結構體。這個適配器沒有對應的driver,在錯綜複雜的i2c架構中,相對於只起到了一個承上啓下的作用,上接i2c控制器的結構體及driver,下接i2c設備的結構體i2c_client和特點的driver。adapter.dev.parent爲i2c控制器對應的device,所以就會出現名爲i2c-0/1/2的設備kobject,只是該設備的bus總線和device_type是:
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
然後調用函數device_register(&adap->dev);來註冊這個device,所以在對應的i2c-0/1/2目錄下也會出現power目錄。
/sys/devices/platform/mt6516-i2c.2/i2c-2/power
i2c設備會通過自動檢測或者事先靜態描述的方式來註冊進系統,不管什麼方式,都會調用到函數:i2c_new_device()
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);
可以看得出來名字是什麼了,例如:2-00aa
#ls -l /sys/devices/platform/mt6516-i2c.2/i2c-2/2-00aa
-rw-r--r-- root root 4096 2010-01-02 06:35 uevent
-r--r--r-- root root 4096 2010-01-02 06:38 name
-r--r--r-- root root 4096 2010-01-02 06:38 modalias
lrwxrwxrwx root root 2010-01-02 06:38 subsystem -> ../../../../../bus/i2c
drwxr-xr-x root root 2010-01-02 06:35 power
lrwxrwxrwx root root 2010-01-02 06:38 driver -> ../../../../../bus/i2c/drivers/mt6516-tpd
三、bus_type、device_driver、device_type、class中的dev_pm_ops方法結構體
在新的linux內核中,已不再有subsystem數據結構了,他的功能被kset代替。
全局變量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,以platform和i2c總線爲例:
@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,
};
從上面的dev_pm_ops結構體中拿出最普遍使用的函數指針來說明一下,對於bus_type它的電源管理是如何實現的。
static int platform_pm_prepare(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
if (drv && drv->pm && 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 && drv->pm->complete)
drv->pm->complete(dev);
}
可以看出這兩個函數都最終是利用了device_driver結構體中的dev_pm_ops函數方法結構體中的對應函數指針。
////////////////////////////////////////////
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;
}
這裏suspend和resume函數也是最終都是調用了device_driver結構體的dev_pm_ops方法結構體中的對應函數指針(device_driver.pm項被初始化),否則使用老式的方法:platform_legacy_suspend(dev, PMSG_SUSPEND)和platform_legacy_resume(dev)。根據這兩個函數的源碼可以看出。一般地,在我們的platform device的platform driver定義中,都是實現了pdrv.suspend和pdrv.resume函數,而並沒有實現pdrv.driver.suspend和pdrv.driver.resume函數,其餘三個函數可以在platform_driver_register()函數中看出:
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總線註冊沒有使用新式的電源管理方法:dev_pm_ops,仍然使用老式的方式:
@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);
}
// 實際上都是調用的i2c_driver結構體的suspend和resume函數。
2. device_type結構體暫時還沒有找到有哪一個模塊使用了新式了dev_pm_ops電源管理方法,一般都是沒有實現這部分。
3. class結構體也沒有找到使用dev_pm_ops方法結構體的地方,先暫時放一放。
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;
};
一般都是實現了platform driver和i2c_driver結構體的suspend和resume函數,並沒有使用新式的電源管理方式。