2023年11月29日发(作者:)

电源管理⽅式的变

,d i2c_d

新版linux系统设备架构中关于电源管理⽅式的变更

based on linux-2.6.32

⼀、设备模型各数据结构中电源管理的部分

linux的设备模型通过诸多结构体来联合描述,如struct devicestruct device_typestruct class

struct device_driverstruct 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;

};

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_,

NULL,

};

static struct attribute_group pm_attr_group = {

.name = "power", // attribute_group结构体的name域不为NULL的话,都会已name建⽴⼀个属性⽬录的

}

举例说明:

我们熟知的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⽬录,同时这3platform设备会依靠

platform_连接件链接到电源管理核⼼链表dpm_list中。

/sys/devices/platform/mt6516-i2c.2/power

⼀个i2c控制器都会在系统中⾄少注册成⼀个适配器(adapter),该结构体将会间接提供给i2c设备的驱动来使⽤,以避免直接使⽤i2c控制器结构 体。这个适配器没有对应的driver,在错综复杂的i2c

构中,相对于只起到了⼀个承上启下的作⽤,上接i2c控制器的结构体及driver,下接 i2c设备的结构体i2c_client和特点的driveri2c控制器对应的device,所以就会出现名 i2c-

0/1/2的设备kobject,只是该设备的bus总线和device_type是:

adap-> = &i2c_bus_type;

adap-> = &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-> = &client->adapter->dev;

client-> = &i2c_bus_type;

client-> = &i2c_client_type;

dev_set_name(&client->dev, "%d-x", 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_typedevice_driverdevice_typeclass中的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,以platformi2c总线为例:

@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)

}

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结构体的suspendresume函数。

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);