12.4 设备驱动中电源管理
一个真实的设备驱动除要处理设备的基本功能外,还需要处理电源管理,主要提供挂起和恢复用的 suspend()、resume()两个函数,对于 platform_driver ,该结构体已经包含这两个成员函数,包含 suspend()、resume()入口的 platform_driver 一般形式如代码清单 12.19。
include/linux/platform_device.h
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state); /*挂起*/
int (*resume)(struct platform_device *);/*恢复*/
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
代码清单 12.19 包含 suspend/resume 的 platform_driver
#ifdef CONFIG_PM
static int xxx_suspend(struct platform_device *pdev, pm_message_t state)
{
...
return 0;
}
static int xxx_resume(struct platform_device *pdev)
{
...
return 0;
}
#else
#define xxx_suspend NULL
#define xxx_resume NULL
#endif
static struct platform_driver xxx_driver = {
.probe = xxx_probe,
.remove = xxx_remove,
.suspend = xxx_suspend,
.resume = xxx_resume,
.driver = {
.name = "xxx",
.owner = THIS_MODULE,
},
};
分析:
上述代码清单中,对于 suspend()、resume()进行了 CONFIG_PM 宏的检查,也就是说内核配置了电源管理的情况下,才定义 suspend()、resume()的实体,否则将它们定义为 NULL。
通常情况下,在 suspend()函数里面会停止设备,并关闭给该设备提供的时钟, 所以在 suspend()函数中经常看到这样的语句:
clk_disable(xxx->clk);
而在 resume()函数中,进行相反的操作:
clk_enable(xxx->clk);
clk_disable()、clk_enable()的具体实现直接依赖于 SoC 的类型,实际上,在 BSP 内为 SoC 内的各个 PLL(锁相环)、分频器和时钟 gate 建立了一颗树,并提供了一组操作时钟的通用 API。因此,在具体的设备驱动中,最好不要直接去修改寄存器来操作时钟,而应该用如下 API:
/* 获得、释放时钟 */
struct clk *clk_get(struct device *dev, const char *id);
void clk_put(struct clk *clk);
/* 使能、禁止时钟 */
int clk_enable(struct clk *clk);
void clk_disable(struct clk *clk);
/* 获得、设置时钟频率 */
unsigned long clk_get_rate(struct clk *clk);
long clk_round_rate(struct clk *clk, unsigned long rate);
int clk_set_rate(struct clk *clk, unsigned long rate);
/* 设置、获得父时钟 */
int clk_set_parent(struct clk *clk, struct clk *parent);
struct clk *clk_get_parent(struct clk *clk);
备注:
在早期内核版本,platform_driver结构体定义如下:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct pm_ext_ops *pm;
struct device_driver driver;
};
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
suspend_late()与 suspend()的区别在于:suspend_late()工作在中断都被禁止的情况下,而且仅有一个 CPU 是活跃的。
resume_early()工作在中断都被禁止的情况下。
绝大多数情况下,设备驱动不提供 suspend_late()和 resume_early()入口。