当前位置: 首页 > news >正文

linux kernel struct clk_init_data结构浅解

在Linux内核的时钟子系统中,struct clk_init_data是连接时钟硬件描述与框架管理的核心数据结构。

它用于初始化时钟提供者(Clock Provider)的基本信息,是时钟控制器驱动(如PLL、分频器、门控时钟等)向内核时钟框架注册时钟的关键载体。

一、struct clk_init_data 结构定义

struct clk_init_data定义在 include/linux/clk-provider.h中,其核心作用是向时钟框架描述一个时钟的基本属性和行为。典型定义如下(不同内核版本可能略有差异):

struct clk_init_data {const char          *name;          // 时钟名称(全局唯一)const char          **parent_names;// 父时钟名称数组(NULL表示无父时钟)u8                  num_parents;    // 父时钟数量const struct clk_ops *ops;          // 时钟操作函数集(必须实现核心操作)unsigned long       flags;          // 时钟属性标志(如关键时钟、不可睡眠等)void                (*hw_init_cb)(struct clk_hw *); // 硬件初始化回调(可选)
};

注:部分内核版本可能包含扩展成员(如 clk_hw指针),但核心成员保持稳定。

二、成员详细解析

1. name:时钟名称
  • 作用​:时钟在系统中的唯一标识符,其他驱动通过此名称调用 clk_get()获取时钟句柄。

  • 约束​:必须全局唯一,否则注册时会报错(-EEXIST)。

2. parent_namesnum_parents:父时钟信息
  • 作用​:定义该时钟的父时钟(时钟树中的上游时钟)。parent_names是父时钟名称的字符串数组,num_parents是数组长度。

  • 特殊场景​:

    • 若时钟无父时钟(如晶振源),parent_names设为 NULLnum_parents设为0。

    • 若父时钟是动态生成的(如通过 of_clk_add_provider注册的分层时钟),需确保父时钟名称已在系统中注册。

  • 示例​:一个分频器时钟的父时钟是 "pll0",则 parent_names = "pll0"num_parents = 1

3. ops:时钟操作函数集
  • 作用​:指向 struct clk_ops的指针,定义时钟的核心操作(如设置频率、使能/禁用、获取父时钟等)。​必须根据时钟类型实现必要函数

  • 常见操作函数​:

    • prepare/unprepare:硬件初始化/去初始化(如开启时钟门控的电源)。

    • enable/disable:运行时使能/禁用时钟(低功耗场景常用)。

    • recalc_rate:重新计算当前时钟频率(基于父时钟和分频系数)。

    • round_rate:计算最接近目标频率的可设置频率。

    • set_rate:设置时钟到目标频率(可能触发硬件寄存器修改)。

    • get_parent/set_parent:获取/设置父时钟索引(多父时钟场景)。

  • 注意​:若未实现某些函数,需将对应指针设为 NULL(但 recalc_rate通常是必需的)。

4. flags:时钟属性标志
  • 作用​:通过位掩码定义时钟的特殊属性,影响框架对时钟的管理行为。

  • 常见标志​:

    • CLK_SET_RATE_GATE:设置频率时必须先禁用时钟(避免动态调整导致异常)。

    • CLK_SET_PARENT_GATE:设置父时钟时必须先禁用时钟。

    • CLK_SET_RATE_NO_REPARENT:设置频率时不改变父时钟(仅调整内部参数)。

    • CLK_IS_BASIC:标记为基础时钟(如晶振、PLL,不依赖其他时钟)。

    • CLK_IGNORE_UNUSED:即使未被使用也保留时钟(防止被框架自动注销)。

    • CLK_IS_CRITICAL:关键时钟(系统启动时必须启用,不能被禁用)。

  • 示例​:关键时钟需设置 CLK_IS_CRITICAL,避免被错误关闭。

三、使用方法:从定义到注册

struct clk_init_data通常与 struct clk_hw配合使用,最终通过时钟注册函数(如 clk_hw_register)将时钟信息传递给框架。以下是典型步骤:

步骤1:定义时钟硬件结构(struct clk_hw)

struct clk_hw是时钟硬件的抽象,包含 clk_init_data和指向具体硬件操作的指针(如 clk_hw->init指向 clk_init_data)。

struct my_clk_hw {struct clk_hw hw;  // 必须作为第一个成员(强制转换用)void __iomem *base; // 硬件寄存器基地址u32 reg_offset;     // 时钟控制寄存器偏移
};
步骤2:填充 struct clk_init_data

根据时钟类型(如门控时钟、分频器)填充 init_data,重点关注名称、父时钟、操作函数和标志。

static const char * const my_clk_parents[] = { "pll0" }; // 父时钟名称static const struct clk_init_data my_clk_init_data = {.name = "my_custom_clk",       // 时钟名称.parent_names = my_clk_parents,// 父时钟数组.num_parents = 1,              // 父时钟数量.ops = &my_clk_ops,            // 自定义操作函数集.flags = CLK_SET_RATE_GATE | CLK_IGNORE_UNUSED, // 标志
};
步骤3:实现 clk_ops 操作函数

根据时钟类型实现必要的操作函数。以门控时钟为例:

static const struct clk_ops my_clk_ops = {.enable = my_clk_enable,       // 使能时钟.disable = my_clk_disable,     // 禁用时钟.recalc_rate = my_clk_recalc_rate, // 计算当前频率
};// 使能函数:设置寄存器的使能位
static int my_clk_enable(struct clk_hw *hw)
{struct my_clk_hw *my_hw = to_my_clk_hw(hw);writel_relaxed(1 << my_hw->reg_offset, my_hw->base + REG_ENABLE);return 0;
}// 禁用函数:清除使能位
static void my_clk_disable(struct clk_hw *hw)
{struct my_clk_hw *my_hw = to_my_clk_hw(hw);writel_relaxed(0 << my_hw->reg_offset, my_hw->base + REG_ENABLE);
}// 计算频率:从寄存器读取分频系数,结合父时钟频率计算
static unsigned long my_clk_recalc_rate(struct clk_hw *hw,unsigned long parent_rate)
{struct my_clk_hw *my_hw = to_my_clk_hw(hw);u32 div = readl_relaxed(my_hw->base + my_hw->reg_offset) & 0xFF;return parent_rate / (div + 1); // 假设分频系数为div+1
}
步骤4:注册时钟

通过 clk_hw_registerdevm_clk_hw_register注册时钟,框架会根据 init_data初始化时钟核心(struct clk_core)并加入时钟树。

static int my_clk_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct my_clk_hw *my_hw;struct resource *res;int ret;// 分配硬件结构内存my_hw = devm_kzalloc(dev, sizeof(*my_hw), GFP_KERNEL);if (!my_hw)return -ENOMEM;// 获取寄存器基地址res = platform_get_resource(pdev, IORESOURCE_MEM, 0);my_hw->base = devm_ioremap_resource(dev, res);if (IS_ERR(my_hw->base))return PTR_ERR(my_hw->base);// 关联 clk_init_data 到 clk_hwmy_hw->hw.init = &my_clk_init_data;my_hw->reg_offset = 0x10; // 假设控制寄存器偏移为0x10// 注册时钟ret = devm_clk_hw_register(dev, &my_hw->hw);if (ret) {dev_err(dev, "Failed to register clock");return ret;}// 可选:将时钟暴露给设备树(通过of_clk_add_hw_provider)return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get, &my_hw->hw);
}

五、注意事项

  1. 名称唯一性​:name必须全局唯一,否则 clk_get()会失败。

  2. 父时钟依赖​:父时钟需先于当前时钟注册(通过设备树顺序或显式依赖声明)。

  3. 操作函数完整性​:至少实现 recalc_rate,否则无法获取时钟频率。

  4. 错误处理​:注册时检查返回值(如 -EEXIST表示名称冲突,-EINVAL表示父时钟无效)。

惠州西湖

http://www.dtcms.com/a/553289.html

相关文章:

  • ▲各类通信算法的FPGA开发学习教程——总目录
  • 2025企业秋招:AI笔试监考如何重塑秋招公平性?
  • Rust开发之常用标准库Trait实践(Display、From/Into)
  • XML与HTML
  • 太原做网站需要多少钱网页设计网站怎么放到域名里
  • 网站开发 费用怎么用PS做网站广告图
  • 算法专题十八:FloodFill算法(使用dfs)
  • 【11408学习记录】考研数学速成:n维随机变量分布函数详解(从定义到边缘分布一网打尽)
  • 网络安全应用题3:网络攻击与防范
  • 做网站设计赚钱吗做攻略的网站好
  • 用react和ant.d做的网站例子宣传推广方式
  • 网店网站设计php网站开发教学
  • 鸿蒙元服务深度实践:跨端唤醒与状态共享的设计模式
  • 【Linux】信号机制详解:进程间通信的核心
  • 当一家车企出现在AI顶会
  • 解锁AI交互新范式:MCP(Model Context Protocol)如何重塑模型上下文管理
  • 保定 网站制作网站策划ppt
  • C#知识学习-019(泛型类型约束关键字)
  • ioDraw实测:AI加持的全能图表工具,免费又好用?
  • GD32F407VE天空星开发板的188数码管
  • 时硕科技,隐形冠军的修炼之道
  • 普通企业网站建设嘉兴网站建设搭建
  • 论文网站开发贵州城乡住房建设网站
  • 计算机毕业设计 基于Python的电商用户行为分析系统 Django 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • EtherNet/IP转EtherNet/IP协议转换网关驱动:欧姆龙与罗克韦尔PLC通讯配置完整案例
  • 天津网站建设价位邵东建设公司网站哪家好
  • 鸿蒙Flutter三方库适配指南:07.插件开发
  • 从大厂到中小公司,活下去的五个生存法则
  • Flink State Checkpointing
  • 华为开源自研AI框架昇思MindSpore应用案例:跑通Vision Transformer图像分类