时钟子系统

linux时钟子系统

框架结构

  • 内核使用CCF(common clock framework)对时钟进行管理,linux时钟子系统框架结构如下图所示:
    pic
    • 右侧为时钟的提供者clock provider,比如各种有/无源晶振
    • 中间为CCF,将时钟与对应的设备匹配
    • 左侧为时钟的使用者clock consumer,即各个设备

clock consumer

  • clock子系统向clock comsumer提供一套通用API用于访问底层时钟,从而屏蔽底层硬件的差异,在进行设备驱动的编写时可以使用以下API函数对设备所需的时钟进行操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    struct clk *clk_get(struct device *dev, const char *id);
    struct clk *devm_clk_get(struct device *dev, const char *id);

    int clk_prepare(struct clk *clk); //启动clock前的准备工作,可能会睡眠
    void clk_unprepare(struct clk *clk); //停止clock之后的善后工作,可能会睡眠

    int clk_enable(struct clk *clk); //启动clock,不会睡眠
    void clk_disable(struct clk *clk); //停止clock,不会睡眠

    /*clock的频率获取和设置*/
    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);

    /*获取或者设置clock的parent*/
    int clk_set_parent(struct clk *clk, struct clk *parent);
    struct clk *clk_get_parent(struct clk *clk);

    int clk_prepare_enable(struct clk *clk); //将preparea和enable组合起来
    void clk_disable_unprepare(struct clk *clk);//将unprepare和disable组合起来
  • 需要注意的是,在进行设备驱动编写时,如果需要对时钟进行操作,必须先使用clk_get或者devm_clk_get函数获取对应的时钟,会返回一个struct clk *结构体,即为一个clk对象,在使用其他API时均通过传入此clk对象对具体的clk进行操作。

CCF(common clock framework)

  • CCF是linux时钟子系统的核心,将其抽象为一个struct clk_core,每一个注册的时钟设备都对应一个clk_core
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     struct clk_core {
    const char *name;
    const struct clk_ops *ops;
    struct clk_hw *hw;
    struct module *owner;
    struct device *dev;
    struct device_node *of_node;
    struct clk_core *parent;
    struct clk_parent_map *parents;
    u8 num_parents;
    u8 new_parent_index;
    unsigned long rate;
    unsigned long req_rate;
    unsigned long new_rate;
    struct clk_core *new_parent;
    struct clk_core *new_child;
    unsigned long flags;
    bool orphan;
    bool rpm_enabled;
    bool need_sync;
    bool boot_enabled;
    unsigned int enable_count;
    unsigned int prepare_count;
    unsigned int protect_count;
    unsigned long min_rate;
    unsigned long max_rate;
    unsigned long accuracy;
    int phase;
    struct clk_duty duty;
    struct hlist_head children;
    struct hlist_node child_node;
    struct hlist_head clks;
    unsigned int notifier_count;
    #ifdef CONFIG_DEBUG_FS
    struct dentry *dentry;
    struct hlist_node debug_node;
    #endif
    struct kref ref;
    };

clock provider

  • 时钟子系统将所有的clock_provider分为6类
    • fixed_clk:固定频率时钟
    • fixed_factor_clk:固定分频系数时钟
    • devider:分频器
    • gate:门,控制时钟使能/失能
    • mux:多路选择器,用于选择时钟源
    • composite:从多个时钟中选一个父时钟,并对其进行分频
数据结构
  • 将所有的clock_provider中共有的硬件特性抽象出来,使用struct clk_hw描述一个具体的硬件时钟

    1
    2
    3
    4
    5
     struct clk_hw {
    struct clk_core *core; //和clk_hw互相包含 point back
    struct clk *clk; //指向consummer中的clk实例,用于 call clk API
    const struct clk_init_data *init; //contains the init data shared with the common clock framework
    };
    • struct clk_init_data描述时钟的初始化数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      struct clk_init_data {
      const char *name;
      const struct clk_ops *ops; //操作函数集合 consummer调用的API函数实际上就是此函数集的抽象
      /* Only one of the following three should be assigned */
      const char * const *parent_names;
      const struct clk_parent_data *parent_data;
      const struct clk_hw **parent_hws;
      u8 num_parents;
      unsigned long flags;
      };
      • struct clk_ops时钟操作函数集合,定义在kernel/include/linux/clk-provider.h
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        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);
        int (*save_context)(struct clk_hw *hw);
        void (*restore_context)(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 rate, unsigned long *parent_rate);
        int (*determine_rate)(struct clk_hw *hw, struct clk_rate_request *req);
        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 rate, unsigned long parent_rate);
        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);
        int (*get_phase)(struct clk_hw *hw);
        int (*set_phase)(struct clk_hw *hw, int degrees);
        int (*get_duty_cycle)(struct clk_hw *hw, struct clk_duty *duty);
        int (*set_duty_cycle)(struct clk_hw *hw, struct clk_duty *duty);
        int (*init)(struct clk_hw *hw);
        void (*terminate)(struct clk_hw *hw);
        void (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
        int (*pre_rate_change)(struct clk_hw *hw, unsigned long rate, unsigned long new_rate);
        int (*post_rate_change)(struct clk_hw *hw, unsigned long old_rate,unsigned long rate);
        };
      • 常用的函数,需要在clock驱动中实现
      function description
      recalc_rate 通过查询硬件,重新计算此时钟的速率。可选,但建议——如果未设置此操作,则时钟速率初始化为0
      round_rate 给定目标速率作为输入,返回时钟实际支持的最接近速率
      set_rate 更改此时钟的速率,请求的速率由第二个参数指定,该参数通常应该是调用.round_rate返回;第三个参数给出了父速率,这对大多数.set_rate实现有帮助;成功返回0,否则返回-EERROR
      enable
      disable
  • 各个数据结构之间的关系如下图所示:

    relationship

注册方式

  • fixed rate clock
    • 这一类clock具有固定的频率,不能开关、不能调整频率、不能选择parent,是最简单的一类clock。可以直接通过 DTS 配置的方式支持。也可以通过接口,可以直接注册 fixed rate clock,如下:
      1
      2
      3
      4
      CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
      struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
      const char *parent_name, unsigned long flags,
      unsigned long fixed_rate);
  • gate clock
    • 这类clock只能选择开关,通过提供.enable.disable回调函数实现,可通过下面接口进行注册:
      1
      2
      3
      4
      struct clk *clk_register_gate(struct device *dev, const char *name,
      const char *parent_name, unsigned long flags,
      void __iomem *reg, u8 bit_idx, u8 clk_gate_flags,
      spinlock_t *lock);
  • divider clock
    • 这类clock可以设置分频值,因此会提供.recal_rate.set_rate.round_date,可通过下面两个接口注册
      1
      2
      3
      4
      5
      6
      7
      8
      9
      struct clk *clk_register_divider(struct device *dev, const char *name, 
      const char *parent_name, unsigned long flags,
      void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
      spinlock_t *lock);

      struct clk *clk_register_divider_table(struct device *dev, const char *name,
      const char *parent_name, unsigned long flags, void __iomem *reg,
      u8 shift, u8 width, u8 clk_divider_flags,
      const struct clk_div_table *table, spinlock_t *lock);
  • mux clock
    • 这类clock可以设置多个parent,实现.get_parent.set_parent.recal_rate回调函数,可通过下面两个接口注册:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      struct clk *clk_register_mux(struct device *dev, const char *name,
      const char **parent_names, u8 num_parents, unsigned long flags,
      void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags,
      spinlock_t *lock);

      struct clk *clk_register_mux_table(struct device *dev, const char *name,
      const char **parent_names, u8 num_parents, unsigned long flags,
      void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags,
      u32 *table, spinlock_t *lock);
  • fixed factor clock
    • 这一类clock具有固定的factor(即multiplier和divider),clock的频率是由parent clock的频率,乘以mul,除以div,多用于一些具有固定分频系数的clock。由于parent clock的频率可以改变,因而fix factor clock也可该改变频率,因此也会提供.recalc_rate.set_rate.round_rate等回调。可通过下面接口注册:
      1
      2
      3
      struct clk *clk_register_fixed_factor(struct device *dev, const char *name, 
      const char *parent_name, unsigned long flags,
      unsigned int mult, unsigned int div);
  • composite clock
    • 其余各类clock的组合
      1
      2
      3
      4
      5
      6
      struct clk *clk_register_composite(struct device *dev, const char *name,
      const char **parent_names, int num_parents,
      struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
      struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
      struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
      unsigned long flags);

时钟子系统
http://example.com/2023/04/09/时钟子系统/
作者
Hector
发布于
2023年4月9日
许可协议