第12章工程中的 Linux设备驱动之Linux 设备驱动的电源管理

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。

扫描二维码关注公众号,回复: 1598750 查看本文章

    通常情况下,在 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()入口。


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80670425
今日推荐