Linux内核时钟

系统的时钟结构

低频时钟

低频的作用是给整个系统提供一个基础的时钟源,低频时钟通常有四种,HSE(外部高配晶振)、HSI(内部高配晶振)、LSE(外部低频晶振)、LSI(内部低频晶振)。

锁相环

锁相环也叫PLL,作用是将低频时钟倍频到高频、经过PLL的时钟通常会达到几百兆或者1G以上。

分频器

分频器也叫DIV/PSC,作用是将时钟进行分配,得到频率不一样的时钟。

时钟复用器

时钟复用器也叫MUX,作用是选择不同的时钟源

门控

门控也叫GATE,作用是控制时钟的开关

内核CLK对象

clk_hw

c 复制代码
struct clk_hw {
      struct clk_core *core;
      struct clk *clk;
      const struct clk_init_data *init;
};

struct clk_hw 是从 struct clk 遍历到对应硬件特定结构的句柄。clk_hw通常嵌入到一个自定义的时钟结构体里面。

clk_init_data

c 复制代码
struct clk_init_data {
      const char              *name;
      const struct clk_ops    *ops;
      /* 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;
};
  • name:时钟名字
  • ops:时钟操作函数
  • parent_names:父时钟名字
  • num_parents:有多少个父时钟
  • flags:标志位

clk_ops

时钟操作函数,包括读取频率,设置频率,使能时钟等操作

c 复制代码
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);
};
  • enable:使能时钟
  • disable:不使能时钟
  • is_enabled:查询时钟是否已经使能
  • recalc_rate:重新计算当前时钟的实际输出频率
  • round_rate:四舍五入:给定一个目标频率和父时钟频率,返回本时钟实际能实现的最近频率(不改硬件,只查询)。
  • determine_rate:round_rate 的升级版,更灵活。传入 struct clk_rate_request(含目标频率、父时钟频率、可选父时钟索引),驱动可以同时建议改父时钟来达到更精确的频率。
  • set_rate:设置时钟频率
  • set_parent:选择父时钟
  • get_parent:获取父时钟

clk flags

可以通过设置标志位来识别该时钟的特性

c 复制代码
#define CLK_SET_RATE_GATE      BIT(0) /* must be gated across rate change */
#define CLK_SET_PARENT_GATE    BIT(1) /* must be gated across re-parent */
#define CLK_SET_RATE_PARENT    BIT(2) /* propagate rate change up one level */
#define CLK_IGNORE_UNUSED      BIT(3) /* do not gate even if unused */
                    /* unused */
                    /* unused */
#define CLK_GET_RATE_NOCACHE   BIT(6) /* do not use the cached clk rate */
#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
#define CLK_RECALC_NEW_RATES   BIT(9) /* recalc rates after notifications */
#define CLK_SET_RATE_UNGATE    BIT(10) /* clock needs to run to set rate */
#define CLK_IS_CRITICAL        BIT(11) /* do not gate, ever */
/* parents need enable during gate/ungate, set rate and re-parent */
#define CLK_OPS_PARENT_ENABLE  BIT(12)
/* duty cycle call may be forwarded to the parent clock */
#define CLK_DUTY_CYCLE_PARENT  BIT(13)

时钟初始化宏

内核定义了很多的时钟初始化宏,可以帮助驱动开发者快速的初始化一个时钟,驱动开发者需要根据系统的时钟框架选用不同的初始化宏。

c 复制代码
#define CLK_HW_INIT(_name, _parent, _ops, _flags)        \
    (&(struct clk_init_data) {                           \
        .flags        = _flags,                          \
        .name         = _name,                           \
        .parent_names = (const char *[]) { _parent },    \
        .num_parents  = 1,                               \
        .ops          = _ops,                            \
    })

#define CLK_HW_INIT_HW(_name, _parent, _ops, _flags)          \
    (&(struct clk_init_data) {                                \
        .flags        = _flags,                               \
        .name         = _name,                                \
        .parent_hws   = (const struct clk_hw*[]) { _parent }, \
        .num_parents  = 1,                                    \
        .ops          = _ops,                                 \
    })

/*
 * This macro is intended for drivers to be able to share the otherwise
 * individual struct clk_hw[] compound literals created by the compiler
 * when using CLK_HW_INIT_HW. It does NOT support multiple parents.
 */
#define CLK_HW_INIT_HWS(_name, _parent, _ops, _flags)          \
    (&(struct clk_init_data) {                                 \
        .flags        = _flags,                                \
        .name         = _name,                                 \
        .parent_hws   = _parent,                               \
        .num_parents  = 1,                                     \
        .ops          = _ops,                                  \
    })

#define CLK_HW_INIT_FW_NAME(_name, _parent, _ops, _flags)      \
    (&(struct clk_init_data) {                                 \
        .flags        = _flags,                                \
        .name         = _name,                                 \
        .parent_data  = (const struct clk_parent_data[]) {     \
                    { .fw_name = _parent },                    \
                  },                                           \
        .num_parents  = 1,                                     \
        .ops          = _ops,                                  \
    })

#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags)     \
    (&(struct clk_init_data) {                                 \
        .flags        = _flags,                                \
        .name         = _name,                                 \
        .parent_names = _parents,                              \
        .num_parents  = ARRAY_SIZE(_parents),                  \
        .ops          = _ops,                                  \
    })

#define CLK_HW_INIT_PARENTS_HW(_name, _parents, _ops, _flags)  \
    (&(struct clk_init_data) {                                 \
        .flags        = _flags,                                \
        .name         = _name,                                 \
        .parent_hws   = _parents,                              \
        .num_parents  = ARRAY_SIZE(_parents),                  \
        .ops          = _ops,                                  \
    })

#define CLK_HW_INIT_PARENTS_DATA(_name, _parents, _ops, _flags) \
    (&(struct clk_init_data) {                                  \
        .flags        = _flags,                                 \
        .name         = _name,                                  \
        .parent_data  = _parents,                               \
        .num_parents  = ARRAY_SIZE(_parents),                   \
        .ops          = _ops,                                   \
    })

#define CLK_HW_INIT_NO_PARENT(_name, _ops, _flags)            \
    (&(struct clk_init_data) {                                \
        .flags          = _flags,                             \
        .name           = _name,                              \
        .parent_names   = NULL,                               \
        .num_parents    = 0,                                  \
        .ops            = _ops,                               \
    })

#define CLK_FIXED_FACTOR(_struct, _name, _parent,             \
            _div, _mult, _flags)                              \
    struct clk_fixed_factor _struct = {                       \
        .div        = _div,                                   \
        .mult       = _mult,                                  \
        .hw.init    = CLK_HW_INIT(_name,                      \
                          _parent,                            \
                          &clk_fixed_factor_ops,              \
                          _flags),                            \
    }

#define CLK_FIXED_FACTOR_HW(_struct, _name, _parent,          \
                _div, _mult, _flags)                          \
    struct clk_fixed_factor _struct = {                       \
        .div        = _div,                                   \
        .mult       = _mult,                                  \
        .hw.init    = CLK_HW_INIT_HW(_name,                   \
                           _parent,                           \
                           &clk_fixed_factor_ops,             \
                           _flags),                           \
    }

/*
 * This macro allows the driver to reuse the _parent array for multiple
 * fixed factor clk declarations.
 */
#define CLK_FIXED_FACTOR_HWS(_struct, _name, _parent,         \
                 _div, _mult, _flags)                         \
    struct clk_fixed_factor _struct = {                       \
        .div        = _div,                                   \
        .mult       = _mult,                                  \
        .hw.init    = CLK_HW_INIT_HWS(_name,                  \
                          _parent,                            \
                          &clk_fixed_factor_ops,              \
                          _flags),                            \
    }

#define CLK_FIXED_FACTOR_FW_NAME(_struct, _name, _parent,     \
                 _div, _mult, _flags)                         \
    struct clk_fixed_factor _struct = {                       \
        .div        = _div,                                   \
        .mult       = _mult,                                  \
        .hw.init    = CLK_HW_INIT_FW_NAME(_name,              \
                              _parent,                        \
                              &clk_fixed_factor_ops,          \
                              _flags),                        \
    }

clk_hw_onecell_data

c 复制代码
struct clk_hw_onecell_data {
      unsigned int num;        // 时钟总数
      struct clk_hw *hws[];    // 柔性数组,存放 struct clk_hw 指针
};

这个结构体用于将系统的时钟全部放到一个hws的结构体里面,然后一次性全部注册进时钟框架

内核CLK注册

clk_hw_register

c 复制代码
int clk_hw_register(struct device *dev, struct clk_hw *hw)

这个函数的作用是将时钟注册进时钟框架

clk_hw_register_clkdev

c 复制代码
int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
      const char *dev_id)

把时钟加入 clkdev 查找表,让消费者可以通过名字找到对应的时钟

of_clk_add_hw_provider

c 复制代码
int of_clk_add_hw_provider(
      struct device_node *np,
      struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data),
      void *data
);

这个函数意思是当驱动通过设备树解析时钟节点时,由get这个函数进行解析,内核默认提供一个函数去解析节点,of_clk_hw_onecell_get

内核CLK API函数

  • clk_get:获取时钟函数
  • clk_prepare_enable:准备+使能
  • clk_set_rate:设置时钟频率
  • clk_get_rate:获取时钟频率
  • clk_set_parent:设置父时钟
  • clk_get_parent:获取父时钟
相关推荐
Brilliantwxx1 小时前
【Linux】自动化构建工具 make 与 Makefile
linux·运维·服务器·开发语言·自动化
灵晔君2 小时前
【Linux】进程(四)——进程虚拟地址空间
linux·c语言·开发语言
张毅2004-10-102 小时前
docker详解:安装docker,docker架构,docker镜像、容器、网络、存储,容器监控,容器日志,综合实验
linux·运维·docker·云原生·云计算
Yyyyyy~2 小时前
[Linux]常用指令1
linux
笑一下蒜了.2 小时前
Linux 存储多路径访问手册|multipath.conf 各配置段详解
android·linux·运维
文优2 小时前
Linux 常用命令速查手册(工作版)
linux·运维·docker
@CLoudbays_Martin113 小时前
Linux 服务器如何查看是否被植入后门?
linux·服务器·网络·安全·github·ssl
拾光Ծ3 小时前
【Linux网络】深入理解网络层:从IP协议格式,子网划分到NAT与路由机制
linux·网络·网络协议·tcp/ip·计算机网络
zho_uzhou4 小时前
ubuntu服务端运行vue网页步骤
linux·服务器·vue.js·ubuntu