Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3029881
  • 博文数量: 396
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4209
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-04 13:04
文章分类

全部博文(396)

文章存档

2022年(1)

2021年(2)

2020年(8)

2019年(24)

2018年(135)

2017年(158)

2016年(68)

我的朋友

分类: 嵌入式

2016-09-22 21:57:31

linux clk时钟源管理驱动代码

1.       硬件资源越来越庞大和复杂,内核的另一个挑战就是要便捷的管理这些资源。同时,面对如此之多的平台不同的CPU,管理机制需要统一适用,这就需要对资源的管理抽象到更加通用的层次。CPU中各个模块都需要时钟clock,内核需要一种机制能通用于所有的平台,方便的管理CPU上所有的clock资源。这里分析Linuxclock的管理。即Linux最新版本添加的COMMON_CLOCK通用时钟架构
Linux version: 3.14.1,涉及的源文件有:

include/linux/clk.h 这个是驱动中需要包含的头文件,包含了所有驱动可以使用的clock接口API,驱动开发中需要引入这个头文件

drivers/clk/clk.h 几乎没用,就两行代码。

drivers/clk/clk.c 这个是clock接口的实现文件,include/linux/clk.h中声明的所有接口函数都在这个文件中实现。包括clock的注册、clock频率设置、clock父子关系设置、clock复用设置等。

drivers/clk/clkdev.c这个文件实现了clock的查找表项的建立和查找。实现了典型的clkdev_add clk_get


2.    时钟通用架构的几个重要数据结构

struct clk {

         const char                  *name;

         const struct clk_ops         *ops;

         struct clk_hw            *hw;

         struct module           *owner;

         struct clk           *parent;

         const char                  **parent_names;

         struct clk           **parents;

         u8                       num_parents;

         u8                       new_parent_index;

         unsigned long           rate;

         unsigned long           new_rate;

         struct clk           *new_parent;

         struct clk           *new_child;

         unsigned long           flags;

         unsigned int              enable_count;

         unsigned int              prepare_count;

         unsigned long           accuracy;

         struct hlist_head     children;

         struct hlist_node     child_node;

         unsigned int              notifier_count;

#ifdef CONFIG_DEBUG_FS

         struct dentry             *dentry;

#endif

                  struct kref                  ref;

};

这个结构定义在clk-private.h中,clk-private.h在任何情况下都不应该在自己的源文件中引用。几乎只有drivers/clk/clk.c会包含clk-private.h。这个结构不包含任何hardware-specific的成员,但是如何通向hardware层呢?就是通过opshw两个成员。ops指向了某个clockhardware-specific的操作函数,hw则作为操作函数的参数用于获取clockhardware-specific的数据,[hardware-specific的操作函数]+[hardware-specific的数据]=[clock的寄存器的操作]

struct clk_ops {

         int              (*prepare)(struct clk_hw *hw);

         void           (*unprepare)(struct clk_hw *hw);

         int              (*is_prepared)(struct clk_hw *hw);

         void           (*unprepare_unused)(struct clk_hw *hw);

         int              (*enable)(struct clk_hw *hw);

         void           (*disable)(struct clk_hw *hw);

         int              (*is_enabled)(struct clk_hw *hw);

         void           (*disable_unused)(struct clk_hw *hw);

         unsigned long  (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate);

         long           (*round_rate)(struct clk_hw *hw, unsigned long, unsigned long *);

         long           (*determine_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_clk);

         int              (*set_parent)(struct clk_hw *hw, u8 index);

         u8              (*get_parent)(struct clk_hw *hw);

         int              (*set_rate)(struct clk_hw *hw, unsigned long, unsigned long);

         int              (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index);

         unsigned long  (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy);

         void           (*init)(struct clk_hw *hw);

};

这个结构显然用于指向clockhardware-specific的操作函数,clock驱动中根据某个clock的硬件特性需要实现部分操作函数。例如,PLL锁相环的父时钟一般是高频振荡器,输出时钟频率一般来说也是固定的或者说是不需要动态修改的,那么就只要实现enable/disable/set_rate/recalc_rate差不多就可以了。

struct clk_init_data {

         const char                  *name;

         const struct clk_ops         *ops;

         const char                  **parent_names;

         u8                       num_parents;

         unsigned long           flags;

};

clock的初始化数据,在clock驱动中注册clock需为clock提供必要的信息。name初始化为时钟的名字,可以用于clk_getops指向clock驱动中实现的操作函数;如果某个clock有多个可选的父时钟,parent_names是这些父时钟的名字数组;num_parents父时钟名字数组中父时钟的个数;flags时钟的特性标记,可以参见clk-provider.h最开始部分的特性集合。

struct clk_hw {

         struct clk *clk;

         const struct clk_init_data *init;

};

这个clk_hw结构用于联系common部分和hardware-specific部分。任何ops结构中的操作函数的参数都带有clk_hw结构指针。

struct clk_xxx {

         struct clk_hw   hw;

         void __iomem  *reg;

         u8              shift;

         u8              width;

         u8              flags;

         spinlock_t         *lock;

};

这个结构是一个自定义的时钟结构,clock驱动中需要自己定义满足平台需要的时钟结构,成员hw是必须的,其他成员都是根据时钟硬件的特性按需添加hardware-specific的数据。正是由于hw是自定义时钟结构必须的成员,那么总是可以用过定义#define to_clk_xxx(_hw) container_of(_hw, struct clk_xxx, hw)ops的操作函数中通过to_clk_xxx(_hw)来获取自定义时钟结构,即获取clockhardware-specific的数据,因为前面已经说过ops的函数中都有一个hw作为参数。

3.   clocks链表以及查找

每个clk结构还要对应一个struct clk_lookup结构。在初始化时,会将所有的clk_loopup结构添加进入clocks链表中,定义在drivers/clk/clkdev.c

struct clk_lookup {

         struct list_head        node;

         const char                  *dev_id;

         const char                  *con_id;

         struct clk           *clk;

};

clk_lookup
,顾名思义就知道它是用来查找struct clk结构的。有了它,就可以通过设备名或时钟源的名字来找到相应的struct clk结构。链表操作位于drivers/clk/clkdev.c

4.   clockpm

为了省电,当不需要clk时将其关闭,上面的clk_enable/clk_disable实现了此功能。除了关闭clk省电,还可以降低clk频率以达到省电的目的。当系统当前负载较轻,不需要clk跑在那么高的频率时,就可以对该clk降频了。从这些关系可以看到,clk与电源管理,cpufreq等都可能有关联。

5.   clock驱动开发中只需要引入clk-provider.h文件就OK    

阅读(1048) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~