1. 架构介绍
Clock统是Linux内核中专门管理时钟的子系统.
时钟在嵌入式系统中很重要, 它就像人的脉搏一样, 驱动器件工作.
任何一个CPU, 都需要给它提供一个外部晶振, 这个晶振就是用来提供时钟的; 任何一个CPU内部的片上外设, 也需要工作时钟: 例如GPIO控制器, 首先得给它提供工作时钟, 然后才能访问它的寄存器.
如果你去看一个ARM CPU的芯片手册, 你一定能找到一个章节, 专门描述系统时钟, 一般称之为时钟树(clock tree).
芯片手册从硬件的角度上描述了某个CPU的时钟系统是如何设计的, 而Clock子系统从软件的层面来抽象这个设计.
在本章中, 我们首先从硬件的角度来看看一个时钟树的的例子, 然后自己思考一下软件层面该如何设计, 最后看看clock子系统是怎么做的.
2.1 时钟树
如今, 可运行Linux的主流CPU, 都有非常复杂的clock tree, 我们随便拿一个处理器的spec, 查看clock相关的章节, 一定会有一个非常庞大和复杂的树状图. 这个图由clock相关的器件,以及这些器件输出的clock组成.
下图是一个简单的示例:
clock相关的器件包括
用于产生clock的Oscillator(有源振荡器, 也称作谐振荡器)或者Crystal(无源振荡器, 也称晶振)
用于倍频的PLL(锁相环, Phase Locked Loop)
用于分频的divider
用于多路选择的Mux
用于clock enable控制的与门
使用clock的硬件模块(可称作consumer), 例如HW1, 它可能是GPIO控制器
器件用于产生具体的clock, 例如osc_clk. 这些器件的特性如下:
从输入(parent)和输出(children)的角度来看
某些器件没有parent, 有一个或多个输出. 例如osc_clk
某些器件有一个parent, 有一个或多个输出. 例如PLL1, 它的parent是osc_clk, 输出只有一个pll1_clk
某些器件有多个parent, 有一个或多个输出. 例如MUX, 它有多个parent, 输出只有一个hw3_clk
从频率的角度上来看
某些clock的频率是可以调整的, 我们可以通过设置倍频和分频因子来调整输出频率.
这一类clock最常见.
某些clock的频率是固定的, 而且不能开关, 比如osc_clk. 最常见的是24M或25M.
这一类clock称作fixed_rate_clock
某些clock的频率也是固定的, 不过它可以开关.
这一类clock称作gate_clock
某些clock有固定的倍频和分频因子, 它的输出频率跟随parent的变化而变化.
这一类clock称作fixed_factor_clock
在上图的时钟树中, 有些是clock的提供者, 我们可以称之为provider, 例如oscillator, PLLs; 有些是clock的使用者, 我们可以称之为consumer, 例如HW1, HW2, HW3.
在ARM CPU的内部, 时钟树系统用来provide各种各样的时钟; 各片上外设consume这些时钟. 例如时钟树系统负责提供时钟给GPIO控制器, GPIO控制器则消费提供给它的工作时钟.
在设备驱动开发的过程中, 我们经常会遇到的一个问题是: 想要开启某个模块的时钟.
例如开发GPIO的驱动, 在驱动的probe函数中, 我们需要使能GPIO模块的工作时钟.
从软件层面, 我们就是要提供一种机制, 让consumer可以方便的获取/使能/配置/关闭一个时钟.
接下来, 我们看看软件层面上该如何抽象.
2.2 软件抽象
上一节我们介绍了时钟树, 并介绍了时钟的provider和consumer. 一个CPU芯片内部, 会有很多个provider, 也会有很多的consumer. 软件层面需要做的事情就是管理所有这些provider, 并向consumer提供尽量简单的接口使得consumer可以获取/使能/配置/关闭一个时钟.
因此, 我们可以设计这样一个池子, 所有的provider都可以向池子注册, 把自己添加到池子里面. 池子里面可以用一个链表把所有的provider都串起来, 不同的provider以不同的name区分. 当consumer需要获取某个clock的时候, 通过name向池子查询即可.
在这个池子里面, 每一个provider都可以抽象成一个独立的元素, 因此我们最好设计一个数据结构, 来表示每一个元素.
大致逻辑就是这样了, Linux内核的clock子系统基本上就是在干这些事情.
2.3 clock子系统
Linux内核的clock子系统, 按照其职能, 可以大致分为3部分:
向下提供注册接口, 以便各个clocks能注册进clock子系统
在核心层维护一个池子, 管理所有注册进来的clocks. 这一部分实现的是通用逻辑, 与具体硬件无关.
向上, 也就是像各个消费clocks的模块的device driver, 提供获取/使能/配置/关闭clock的通用API
clock子系统的结构框图如下, 一些细节你现在可能还不能理解. 不过没关系, 读完后面的章节你就会明白了.
后面的章节, 我们也会分为上述3部分来描述, 在阅读的过程中, 你可以边看边对着下面这张图理解, 这样会更加清晰.
2. clock provider -- 如何注册Clocks
3.1 简介
前文我们介绍了时钟树, 本章要阐述的主要问题就是如何把时钟树产生的这些clocks注册进Linux内核的clock子系统. 换句话说, 就是如何编写clock driver.
在ARM CPU内部, 管理时钟树的也是一个单独的模块, 一般叫PCM(Programmable Clock Management), 编写clock driver其实就是编写PCM的driver.
PCM也是CPU的一个片上外设, 因此它也会借用platform这套机制. 因此我们就需要有platform_device来描述设备, 同时要有与之对应的platform_driver来控制设备. 所谓控制, 就写读写PCM的寄存器来使能/关闭时钟, 设置时钟频率等等. 在platform_driver的probe函数中, 还有一项重要功能, 就是调用clock子系统提供的API, 向clock子系统注册.
下面, 我看看编写clock driver 的大致步骤是怎样的.
3.2 编写clock driver的大致步骤
由前文可知, 首先你得准备一个platform_device, 引入device tree的机制后, platform_device被dts替代了, 因此我们就需要在dts里面描述时钟树.
编写platform_device(DTS node)
那么这个DTS该怎么写呢? 通常有两种方式:
方式一: 将时钟树中所有的clock,抽象为一个虚拟的设备,用一个DTS node表示.
1: /* arch/arm/boot/dts/exynos4210.dtsi */
2: theclock: clock-controller@0x10030000 {
3: compatible = "samsung,exynos4210-clock";
4: reg = <0x10030000 0x20000>;
5: #clock-cells = <1>;
6: };
这种方式跟编写一个普通的片上外设的DTS很类似, 比如GPIO控制器的DTS, 对比一下, 是不是很类似.
从这个例子里面, 我们可以看出一个clock的DTS node的基本语法:
compatible , 决定了与这个node匹配的driver
reg就是用来描述PCM的寄存器
#clock-cells, 这个是clock provider node独有的, 表明在引用此clock时, 需要用几个32位来描述. 什么意思呢?
假设这个clock只有一个输出时钟, 那么#clock-cells = <0>, 我们在引用此clock的时候, 只用指明此clock即可.
引用此clock是什么意思? 引用指的是clock的consumer端. 例如GPIO模块需要工作时钟, 那么我们在编写GPIO的DTS node时, 需要指明它的工作时钟是多少, 这个过程就是引用, 写个简单的例子:
compatible = “yyyy”;
reg = <…. ….>;
……
clocks = <&theclock>; /* 指明/引用某一个clock */
}
假设这个clock有多个输出时钟, 那么#clock-cells = <0>肯定不行, 因为我们在引用此clock的时候, 需要指明到底用哪一个输出时钟.
这个时候#clock-cells 应该为 <1>, 在引用此clock, 就得这样写:
gpio : gpio-controller@xxxx {
compatible = “yyyy”;
reg = <…. ….>;
……
clocks = <&theclock num>; /* 指明/引用某一个clock, num是一个32位的整数, 表明到底用哪一个输出clock */
}
theclock, 此DTS node的lable, clock consumer可以根据该名称引用clock
方式二: 将时钟树中的每一个clock抽象为一个DTS node, 并以树形的结构组织. 这种方式相较与方式一, 能更清晰的抽象出时钟的树形结构, 从逻辑上也更合理, 因此推荐大家使用这种方式.
举个方式二的例子:
1: /* arch/arm/boot/dts/sun4i-a10.dtsi */
2: clocks {
3: #address-cells = <1>;
4: #size-cells = <1>;
5: ranges;
19: osc24M: osc24M@01c20050 {
20: #clock-cells = <0>;
21: compatible = "allwinner,sun4i-osc-clk";
22: reg = <0x01c20050 0x4>;
23: clock-frequency = <24000000>;
24: };
25:
27: #clock-cells = <0>;
28: compatible = "fixed-clock";
29: clock-frequency = <32768>;
30: };
31:
32: pll1: pll1@01c20000 {
33: #clock-cells = <0>;
34: compatible = "allwinner,sun4i-pll1-clk";
35: reg = <0x01c20000 0x4>;
36: clocks = <&osc24M>;
37: };
38:
39: /* dummy is 200M */
40: cpu: cpu@01c20054 {
41: #clock-cells = <0>;
42: compatible = "allwinner,sun4i-cpu-clk";
43: reg = <0x01c20054 0x4>;
44: clocks = <&osc32k>, <&osc24M>, <&pll1>, <&dummy>;
45: };
46:
47: axi: axi@01c20054 {
48: #clock-cells = <0>;
49: compatible = "allwinner,sun4i-axi-clk";
50: reg = <0x01c20054 0x4>;
51: clocks = <&cpu>;
52: };
53:
54: axi_gates: axi_gates@01c2005c {
55: #clock-cells = <1>;
56: compatible = "allwinner,sun4i-axi-gates-clk";
57: reg = <0x01c2005c 0x4>;
58: clocks = <&axi>;
59: clock-output-names = "axi_dram";
60: };
61:
62: ahb: ahb@01c20054 {
63: #clock-cells = <0>;
64: compatible = "allwinner,sun4i-ahb-clk";
65: reg = <0x01c20054 0x4>;
66: clocks = <&axi>;
67: };
68:
69: ahb_gates: ahb_gates@01c20060 {
70: #clock-cells = <1>;
71: compatible = "allwinner,sun4i-ahb-gates-clk";
72: reg = <0x01c20060 0x8>;
73: clocks = <&ahb>;
74: clock-output-names = "ahb_usb0", "ahb_ehci0",
75: "ahb_ohci0", "ahb_ehci1", "ahb_ohci1", "ahb_ss",
76: "ahb_dma", "ahb_bist", "ahb_mmc0", "ahb_mmc1",
77: "ahb_mmc2", "ahb_mmc3", "ahb_ms", "ahb_nand",
78: "ahb_sdram", "ahb_ace", "ahb_emac", "ahb_ts",
79: "ahb_spi0", "ahb_spi1", "ahb_spi2", "ahb_spi3",
80: "ahb_pata", "ahb_sata", "ahb_gps", "ahb_ve",
81: "ahb_tvd", "ahb_tve0", "ahb_tve1", "ahb_lcd0",
82: "ahb_lcd1", "ahb_csi0", "ahb_csi1", "ahb_hdmi",
83: "ahb_de_be0", "ahb_de_be1", "ahb_de_fe0",
84: "ahb_de_fe1", "ahb_mp", "ahb_mali400";
85: };
86:
87: apb0: apb0@01c20054 {
88: #clock-cells = <0>;
89: compatible = "allwinner,sun4i-apb0-clk";
90: reg = <0x01c20054 0x4>;
91: clocks = <&ahb>;
92: };
93:
94: apb0_gates: apb0_gates@01c20068 {
95: #clock-cells = <1>;
96: compatible = "allwinner,sun4i-apb0-gates-clk";
97: reg = <0x01c20068 0x4>;
98: clocks = <&apb0>;
99: clock-output-names = "apb0_codec", "apb0_spdif",
100: "apb0_ac97", "apb0_iis", "apb0_pio", "apb0_ir0",
101: "apb0_ir1", "apb0_keypad";
102: };
103:
104: /* dummy is pll62 */
105: apb1_mux: apb1_mux@01c20058 {
106: #clock-cells = <0>;
107: compatible = "allwinner,sun4i-apb1-mux-clk";
108: reg = <0x01c20058 0x4>;
109: clocks = <&osc24M>, <&dummy>, <&osc32k>;
110: };
111:
112: apb1: apb1@01c20058 {
113: #clock-cells = <0>;
114: compatible = "allwinner,sun4i-apb1-clk";
115: reg = <0x01c20058 0x4>;
116: clocks = <&apb1_mux>;
117: };
118:
119: apb1_gates: apb1_gates@01c2006c {
120: #clock-cells = <1>;
121: compatible = "allwinner,sun4i-apb1-gates-clk";
122: reg = <0x01c2006c 0x4>;
123: clocks = <&apb1>;
124: clock-output-names = "apb1_i2c0", "apb1_i2c1",
125: "apb1_i2c2", "apb1_can", "apb1_scr",
126: "apb1_ps20", "apb1_ps21", "apb1_uart0",
127: "apb1_uart1", "apb1_uart2", "apb1_uart3",
128: "apb1_uart4", "apb1_uart5", "apb1_uart6",
129: "apb1_uart7";
130: };
131: };
osc24M 代表24M的晶振
osc32k 代表32k的慢速时钟
pll1 , 它的父时钟是osc24M, 只有一个输出时钟
cpu , 它的父时钟有多个<&osc32k>, <&osc24M>, <&pll1>, <&dummy>, 只有一个输出时钟
ahb_gates , 它的父时钟只有一个, 是ahb, 但是它的输出时钟有很多个
...... 后面的都类似, 不一一说明了
看见了吧, 这种方式确实能清晰的描述多个clocks的树形关系.
编写platform_driver
有了platform_device之后, 接下来就得编写platform_driver, 在driver里面最重要的事情就是向clock子系统注册.
如何注册呢?
clock子系统定义了clock driver需要实现的数据结构, 同时提供了注册的函数. 我们只需要准备好相关的数据结构, 然后调用注册函数进行注册即可.
这些数据结构和接口函数的定义是在: include/linux/clk-provider.h
需要实现的数据结构是 struct clk_hw, 需要调用的注册函数是struct clk *clk_register(struct device *dev, struct clk_hw *hw). 数据结构和注册函数的细节我们在后文说明.
注册函数会返回给你一个struct clk类型的指针, 在Linux的clock子系统中, 用一个struct clk代表一个clock. 你的CPU的时钟树里有多少个clock, 就会有多少个对应的struct clk.
当你拿到返回结果之后, 你需要调用另外一个API : of_clk_add_provider, 把刚刚拿到的返回结果通过此API丢到池子里面, 好让consumer从这个池子里获取某一个clock.
池子的概念我们在前文讲述过, 除了上述方法, 你还可以用另外一种方法把struct clk添加到池子里面.
如果你要用这种方法, 你会用到clock子系统提供给你的另外几个API, 这些API的定义在: include/linux/clkdev.h
其中最主要的一个API是int clk_register_clkdev(struct clk *, const char *, const char *, ...), 你可以在你的clock driver里面调用这个API, 把刚刚拿到的返回结果通过此API丢到池子里面.
为什么会存在这两种方式呢? 得从consumer的角度来解答这个问题.
我们用GPIO来举个例子, GPIO控制器需要工作时钟, 这个时钟假设叫gpio_clk, 它是一个provider. 你需要把这个provider注册进clock子系统, 并把用于描述这个gpio_clk的struct clk添加到池子里面.
在GPIO控制器的driver代码里, 我们需要获取到gpio_clk这个时钟并使能它, 获取的过程就是向池子查询.
怎么查询? 你可以直接给定一个name, 然后通过这个name向池子查询; 你也可以在GPIO的DTS node里面用clocks = <&theclock>;方式指明使用哪一个clock, 然后通过这种方式向池子查询.
如果consumer是通过name查询, 则对应的添加到池子的API就是clk_register_clkdev
如果consumer是通过DTS查询, 则对应的添加到池子的API就是of_clk_add_provider
那么我在我的clock driver里面到底应该用哪个API向池子添加clk呢?
两者你都应该同时使用, 这样consumer端不管用哪种查询方式都能工作.
读到这里, 建议你回头看看clock子系统的系统框图, 结合框图在琢磨琢磨.
接下来, 我们就会详细介绍这些数据结构和相关的API了.
3.3 主要数据结构
通过前文, 我们知道了clock driver需要实现的一个主要的数据结构struct clk_hw.
与之相关的还有另外几个重要数据结构: struct clk_init_data和struct clk_ops.
下面我们看看这几个数据结构.
struct clk_hw
头文件: include/linux/clk-provider.h
struct clk_hw |
Comment |
struct clk_core *core |
clk_core是clock子系统核心层的一个数据结构, 由核心层代码创建和维护, 一个clk_core对应一个具体的clock. |
struct clk *clk |
clk也是clock子系统核心层的一个数据结构, 同样由核心层代码创建和维护, 它也对应一个具体的clock. clk_core和clk的细节, 放在《clock core》一章中描述 |
const struct clk_init_data *init |
clk_init_data是provider需要实现并填充的一个数据结构, 编写clock driver, 最主要的任务就是实现它 |
struct clk_init_data
头文件: include/linux/clk-provider.h
struct clk_init_data |
Comment |
const char*name |
该clock的name, 系统中会存在很多个clock, 每一个clock都会有一个名称, 可以用名称来区分不同的clock, 因此不能重名 |
const struct clk_ops*ops |
与clock控制相关的ops, 例如enable/disable; set_rate等等. 当consumer端想要操作某个clock时, 最终就会调用到该clock的clk_ops |
const char**parent_names |
该clock的所有parents. 通过name, 就能找到parent是谁了 |
u8num_parents |
一个clock可能有多个parents, num_parents指明到底有几个 |
unsigned longflags |
标志位, 表明该clock的一些特性, 核心层代码会根据不同的flags采取不同的动作. 可选的值如下: #define CLK_SET_RATE_GATEBIT(0) /* must be gated across rate change */ #define CLK_SET_PARENT_GATEBIT(1) /* must be gated across re-parent */ #define CLK_SET_RATE_PARENTBIT(2) /* propagate rate change up one level */ #define CLK_IGNORE_UNUSEDBIT(3) /* do not gate even if unused */ #define CLK_IS_ROOTBIT(4) /* root clk, has no parent */ #define CLK_IS_BASICBIT(5) /* Basic clk, can't do a to_clk_foo() */ #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 */ |
struct clk_ops
头文件: include/linux/clk-provider.h
下述这些ops在.h文件里面都有详细的注释, 可以阅读源代码获取更多信息.
struct clk_ops |
Comment |
int(*prepare)(struct clk_hw *hw) |
为什么要有prepare接口, 直接enable不就行了吗? 从硬件的角度来说, 某些clock, 如果想使能它, 需要等待一段时间. 例如倍频器PLL, 当你使能倍频器之后, 你需要等待几毫秒让倍频器工作平稳. 因为要等待, 软件上就有可能sleep, 也就是休眠. 但是Linux内核中有很多情况下不能休眠, 比如说中断服务器程序. 如果你把所有的操作都放在enable这一个函数里面, 那么enable函数就可能休眠, 因而中断服务程序里面就不能调用enable函数. 但实际情况是, 很多时候, 我们都要求在中断服务程序里面开/关某个clock 怎么办呢? 拆分成2个函数, prepare和enable. prepare负责使能clock之前的准备工作, prepare里面可以休眠, 一旦prepare返回, 就意味着clock已经完全准备好了, 可以直接开/关 enable负责打开clock, 它不能休眠, 这样在中断服务程序中也可以调用enable了 |
void(*unprepare)(struct clk_hw *hw) |
prepare的反函数, un-do prepare里面做的所有事情 |
int(*is_prepared)(struct clk_hw *hw) |
is_prepared, 判断clock是否已经prepared, 可以不提供. clock framework core会维护一个prepare的计数(该计数在clk_prepare调用时加一, 在clk_unprepare时减一), 并依据该计数判断是否prepared |
void(*unprepare_unused)(struct clk_hw *hw) |
自动unprepare unused clocks clock framework core提供一个clk_disable_unused接口, 在系统初始化的late_call中调用, 用于关闭unused clocks, 这个接口会调用相应clock的.unprepare_unused和.disable_unused函数 |
int(*enable)(struct clk_hw *hw) |
使能clock, 此函数不能休眠 当此函数返回时, 代表consumer端可以收到一个稳定, 可用的clock波形了. |
void(*disable)(struct clk_hw *hw) |
禁止clock, 此函数不能休眠 |
int(*is_enabled)(struct clk_hw *hw) |
与is_prepared类似 |
void(*disable_unused)(struct clk_hw *hw) |
与unprepare_unused类似 |
int(*save_context)(struct clk_hw *hw) |
Save the context of the clock in prepration for poweroff |
void(*restore_context)(struct clk_hw *hw) |
Restore the context of the clock after a restoration of power |
unsigned long(*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate) |
以parent clock rate为参数, 从新计算并返回clock rate |
long(*round_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) |
Given a target rate as input, returns the closest rate actually supported by the clock 该接口有点特别, 在返回rounded rate的同时, 会通过一个指针, 返回round后parent的rate. 这和CLK_SET_RATE_PARENT flag有关
当clock consumer调用clk_round_rate获取一个近似的rate时, 如果该clock没有提供.round_rate函数, 有两种方法: 在没有设置CLK_SET_RATE_PARENT标志时, 直接返回该clock的cache rate 如果设置了CLK_SET_RATE_PARENT标志, 则会询问parent, 即调用clk_round_rate获取parent clock能提供的、最接近该rate的值. 这是什么意思呢? 也就是说, 如果parent clock可以得到一个近似的rate值, 那么通过改变parent clock, 就能得到所需的clock |
long(*determine_rate)(struct clk_hw *hw, unsigned long rate, unsigned long min_rate, unsigned long max_rate, unsigned long *best_parent_rate, struct clk_hw **best_parent_hw) |
与round_rate类似, 暂时不清楚它俩有什么区别. |
int(*set_parent)(struct clk_hw *hw, u8 index) |
Change the input source of this clock 有的clocks可能有多个parents, 那到底用哪一个呢? 由参数index决定 |
u8(*get_parent)(struct clk_hw *hw) |
Queries the hardware to determine the parent of a clock 返回值是一个u8类型的变量, 它是一个index, 通过它可以查找到对应的parent. 所有的parents都存储在.parent_names or .parents arrays里面
实际上, 此函数会读取硬件寄存器, 然后把寄存器的值转变为对应的index |
int(*set_rate)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) |
Change the rate of this clock The requested rate is specified by the second argument, which should typically be the return of .round_rate call
The third argument gives the parent rate which is likely helpful for most .set_rate implementation |
int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index) |
Change the rate and the parent of this clock This callback is optional (and unnecessary) for clocks with 0 or 1 parents as well as for clocks that can tolerate switching the rate and the parent separately via calls to .set_parent and .set_rate |
unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy) |
Recalculate the accuracy of this clock The clock accuracy is expressed in ppb (parts per billion) |
int(*get_phase)(struct clk_hw *hw) |
Queries the hardware to get the current phase of a clock Returned values are 0-359 degrees on success, negative error codes on failure |
int(*set_phase)(struct clk_hw *hw, int degrees) |
Shift the phase this clock signal in degrees specified by the second argument Valid values for degrees are 0-359 Return 0 on success, otherwise -EERROR |
void(*init)(struct clk_hw *hw) |
Perform platform-specific initialization magic 不过从代码注释来看, 内核推荐你不要实现这个接口函数, 后面可能会遗弃 |
int(*debug_init)(struct clk_hw *hw, struct dentry *dentry) |
debugfs相关, 这里不细述 细节可以看代码注释 |
3.4 主要API说明
Linux clock子系统向下提供几个重要的API, 下面我挨个看下这些API的细节.
clk_register / devm_clk_register
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk.c
/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the primary interface for populating the clock tree with new
* clock nodes. It returns a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjuction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw);
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw);
void clk_unregister(struct clk *clk);
void devm_clk_unregister(struct device *dev, struct clk *clk);
clk_register是clock子系统提供的注册clock的最基础的API函数, 后文描述的其它APIs都是对它的封装.
devm_clk_register是clk_register的devm版本, devm机制在《设备模型》一文中有详述.
要向系统注册一个clock也很简单, 准备好clk_hw结构体, 然后调用clk_register接口即可.
不过, clock framework所做的远比这周到, 它基于clk_register, 又封装了其它接口, 在向clock子系统注册时, 连struct clk_hw都不需要关心, 而是直接使用类似人类语言的方式.
也就是说, 实际在编写clock driver的时候, 我们不会直接使用clk_register接口, 只需要调用下面的某个API即可.
下文我们一一介绍这些API.
clk_register_fixed_rate
clock有不同的类型, 有的clock频率是固定的, 不可调整的, 例如外部晶振, 频率就是固定的(24M / 25M). 这种类型的clock就是fixed_rate clock.
fixed_rate clock具有固定的频率, 不能开关、不能调整频率、不能选择parent、不需要提供任何的clk_ops回调函数, 是最简单的一类clock.
如果要注册这种类型的clock, 直接调用本API, 传递相应的参数给本API即可. API的细节如下:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-fixed-rate.c
/*
* DOC: Basic clock implementations common to many platforms
*
* Each basic clock hardware type is comprised of a structure describing the
* clock hardware, implementations of the relevant callbacks in struct clk_ops,
* unique flags for that hardware type, a registration function and an
* alternative macro for static initialization
*/
/**
* struct clk_fixed_rate - fixed-rate clock
* @hw: handle between common and hardware-specific interfaces
* @fixed_rate: constant frequency of clock
*/
struct clk_fixed_rate {
struct clk_hw hw;
unsigned long fixed_rate;
unsigned long fixed_accuracy;
u8 flags;
};
extern const struct clk_ops clk_fixed_rate_ops;
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned long fixed_rate);
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy);
void of_fixed_clk_setup(struct device_node *np);
若想注册一个fixed rate clock, 除了你可以在自己的clock driver里面手动调用clk_register_fixed_rate之外, 还有一种更简单的方式, 那就是用DTS.
你可以在DTS里面用如下方式描述一个fixed rate clock:
26: osc32k: osc32k {
27: #clock-cells = <0>;
28: compatible = "fixed-clock";
29: clock-frequency = <32768>;
clock-accuracy = xxxx;
clock-output-names = xxxx;
30: };
关键地方在于compatible一定要是”fixed-clock”
“drivers/clk/clk-fixed-rate.c”中的of_fixed_clk_setup会负责匹配这个compatible, 这个C文件是clock子系统实现的.
of_fixed_clk_setup会解析3个参数: clock-frequency, clock-accuracy, clock-output-names
clock-frequency是必须的, 另外2个参数可选.
参数解析完毕之后, 会调用clk_register_fixed_rate_with_accuracy向系统注册一个clock.
clk_register_gate
这一类clock只可开关(会提供.enable/.disable回调), 可使用下面接口注册:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-gate.c
/**
* struct clk_gate - gating clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register controlling gate
* @bit_idx: single bit controlling gate
* @flags: hardware-specific flags
* @lock: register lock
*
* Clock which can gate its output. Implements .enable & .disable
*
* Flags:
* CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to
* enable the clock. Setting this flag does the opposite: setting the bit
* disable the clock and clearing it enables the clock
* CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit
* of this register, and mask of gate bits are in higher 16-bit of this
* register. While setting the gate bits, higher 16-bit should also be
* updated to indicate changing gate bits.
*/
struct clk_gate {
struct clk_hw hw;
void __iomem *reg;
u8 bit_idx;
u8 flags;
spinlock_t *lock;
};
#define CLK_GATE_SET_TO_DISABLE BIT(0)
#define CLK_GATE_HIWORD_MASK BIT(1)
extern const struct clk_ops clk_gate_ops;
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);
void clk_unregister_gate(struct clk *clk);
clk_register_gate, 它的参数列表如下:
name: clock的名称
parent_name: parent clock的名称, 没有的话可留空
flags : 参考3.3节 struct clk_hw结构体中对于flags的描述
reg: 控制该clock开关的寄存器地址(虚拟地址)
bit_idx: reg中, 第几个bit是控制clock开/关的
clk_gate_flags: gate clock特有的参数. 其中一个可选值是CLK_GATE_SET_TO_DISABLE, 它的意思是1表示开还是0表示开, 类似于翻转位.
lock: 如果clock开关时需要互斥, 可提供一个spinlock.
clock子系统并没有定义类似fixed rate clock的DTS处理方式.
如果你想借用DTS, 也很简单. 如下:
ehrpwm1_tbclk: ehrpwm1_tbclk@44e10664 {
#clock-cells = <0>;
compatible = "ti,gate-clock";
clocks = <&l4ls_gclk>;
ti,bit-shift = <1>;
reg = <0x0664>;
};
注意它的compatible, 自己实现一个driver, 匹配这个compatible. 然后在driver里面解析DTS相关参数并调用clk_register_gate API 即可.
clk_register_divider / clk_register_divider_table
这一类clock可以设置分频值(因而会提供.recalc_rate/.set_rate/.round_rate回调), 可通过下面两个接口注册:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-divider.c
/**
* struct clk_divider - adjustable divider clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register containing the divider
* @shift: shift to the divider bit field
* @width: width of the divider bit field
* @table: array of value/divider pairs, last entry should have div = 0
* @lock: register lock
*
* Clock with an adjustable divider affecting its output frequency. Implements
* .recalc_rate, .set_rate and .round_rate
*
* Flags:
* CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the
* register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is
* the raw value read from the register, with the value of zero considered
* invalid, unless CLK_DIVIDER_ALLOW_ZERO is set.
* CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from
* the hardware register
* CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors. For dividers which have
* CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor.
* Some hardware implementations gracefully handle this case and allow a
* zero divisor by not modifying their input clock
* (divide by one / bypass).
* CLK_DIVIDER_HIWORD_MASK - The divider settings are only in lower 16-bit
* of this register, and mask of divider bits are in higher 16-bit of this
* register. While setting the divider bits, higher 16-bit should also be
* updated to indicate changing divider bits.
* CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded
* to the closest integer instead of the up one.
* CLK_DIVIDER_READ_ONLY - The divider settings are preconfigured and should
* not be changed by the clock framework.
*/
struct clk_divider {
struct clk_hw hw;
void __iomem *reg;
u8 shift;
u8 width;
u8 flags;
const struct clk_div_table *table;
spinlock_t *lock;
u32 context;
};
#define CLK_DIVIDER_ONE_BASED BIT(0)
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
#define CLK_DIVIDER_HIWORD_MASK BIT(3)
#define CLK_DIVIDER_ROUND_CLOSEST BIT(4)
#define CLK_DIVIDER_READ_ONLY BIT(5)
extern const struct clk_ops clk_divider_ops;
unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
unsigned int val, const struct clk_div_table *table,
unsigned long flags);
long divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate, const struct clk_div_table *table,
u8 width, unsigned long flags);
int divider_get_val(unsigned long rate, unsigned long parent_rate,
const struct clk_div_table *table, u8 width,
unsigned long flags);
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);
void clk_unregister_divider(struct clk *clk);
clk_register_divider : 该接口用于注册分频比规则的clock
reg: 控制clock分频比的寄存器
shift: 控制分频比的bit在寄存器中的偏移
width: 控制分频比的bit位数, 默认情况下, 实际的divider值是寄存器值加1.
如果有其它例外, 可使用下面的的flag指示。
clk_divider_flags: divider clock特有的flag, 包括: CLK_DIVIDER_ONE_BASED:
实际的divider值就是寄存器值(0是无效的,除非设置CLK_DIVIDER_ALLOW_ZERO flag)
CLK_DIVIDER_POWER_OF_TWO:
实际的divider值是寄存器值得2次方
CLK_DIVIDER_ALLOW_ZERO:
divider值可以为0(不改变,视硬件支持而定)
clk_register_divider_table : 该接口用于注册分频比不规则的clock,和上面接口比较,差别在于divider值和寄存器值得对应关系由一个table决定. table的原型如下:
struct clk_div_table {
unsigned int val;
unsigned int div;
};
val代表寄存器值, div代表对应的分频值.
同样, clock子系统并没有实现DTS相关接口, 不过你可以自己编写driver去解析DTS并调用API注册.
clk_register_mux
这一类clock可以选择多个parent, 因为会实现.get_parent/.set_parent/.recalc_rate回调, 可通过下面两个接口注册:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-mux.c
/**
* struct clk_mux - multiplexer clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register controlling multiplexer
* @shift: shift to multiplexer bit field
* @width: width of mutliplexer bit field
* @flags: hardware-specific flags
* @lock: register lock
*
* Clock with multiple selectable parents. Implements .get_parent, .set_parent
* and .recalc_rate
*
* Flags:
* CLK_MUX_INDEX_ONE - register index starts at 1, not 0
* CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
* CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this
* register, and mask of mux bits are in higher 16-bit of this register.
* While setting the mux bits, higher 16-bit should also be updated to
* indicate changing mux bits.
* CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired
* frequency.
*/
struct clk_mux {
struct clk_hw hw;
void __iomem *reg;
u32 *table;
u32 mask;
u8 shift;
u8 flags;
spinlock_t *lock;
u8 saved_parent;
};
#define CLK_MUX_INDEX_ONE BIT(0)
#define CLK_MUX_INDEX_BIT BIT(1)
#define CLK_MUX_HIWORD_MASK BIT(2)
#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */
#define CLK_MUX_ROUND_CLOSEST BIT(4)
extern const struct clk_ops clk_mux_ops;
extern const struct clk_ops clk_mux_ro_ops;
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);
void clk_unregister_mux(struct clk *clk);
clk_register_mux : 该接口可注册mux控制比较规则的clock(类似divider clock)
parent_names: 一个字符串数组,用于描述所有可能的parent clock
num_parents: parent clock的个数
reg、shift、width: 选择parent的寄存器、偏移、宽度
clk_mux_flags: mux clock特有的flag
CLK_MUX_INDEX_ONE : 寄存器值不是从0开始,而是从1开始
CLK_MUX_INDEX_BIT : 寄存器值为2的幂
clk_register_mux_table : 该接口通过一个table, 注册mux控制不规则的clock, 原理和divider clock类似, 不再详细介绍
同样, clock子系统并没有实现DTS相关接口, 不过你可以自己编写driver去解析DTS并调用API注册.
clk_register_fixed_factor
这一类clock具有固定的factor(即multiplier和divider), clock的频率是由parent clock的频率, 乘以mul, 除以div, 多用于一些具有固定分频系数的clock.
由于parent clock的频率可以改变, 因而fix factor clock也可以改变频率, 因此也会提供.recalc_rate/.set_rate/.round_rate等回调.
可通过下面接口注册:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-fixed-factor.c
void of_fixed_factor_clk_setup(struct device_node *node);
/**
* struct clk_fixed_factor - fixed multiplier and divider clock
*
* @hw: handle between common and hardware-specific interfaces
* @mult: multiplier
* @div: divider
*
* Clock with a fixed multiplier and divider. The output frequency is the
* parent clock rate divided by div and multiplied by mult.
* Implements .recalc_rate, .set_rate and .round_rate
*/
struct clk_fixed_factor {
struct clk_hw hw;
unsigned int mult;
unsigned int div;
};
extern struct clk_ops clk_fixed_factor_ops;
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);
API 参数比较简单, 不多说了.
另外, clock子系统还提供了此种类型clock的DTS接口.
clk-fixed-factor.c中的of_fixed_factor_clk_setup函数会负责解析DTS. 关于DTS的匹配规则和相关参数, 自己看看源码吧.
clk_register_fractional_divider
这一类和divider clock很像, 唯一的不同在于它可支持到更细的粒度 (可用小数表示分频因子) . 例如 clk = parent / 1.5
API说明如下:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-fractional-divider.c
/**
* struct clk_fractional_divider - adjustable fractional divider clock
*
* @hw: handle between common and hardware-specific interfaces
* @reg: register containing the divider
* @mshift: shift to the numerator bit field
* @mwidth: width of the numerator bit field
* @nshift: shift to the denominator bit field
* @nwidth: width of the denominator bit field
* @lock: register lock
*
* Clock with adjustable fractional divider affecting its output frequency.
*/
struct clk_fractional_divider {
struct clk_hw hw;
void __iomem *reg;
u8 mshift;
u32 mmask;
u8 nshift;
u32 nmask;
u8 flags;
spinlock_t *lock;
};
extern const struct clk_ops clk_fractional_divider_ops;
struct clk *clk_register_fractional_divider(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
u8 clk_divider_flags, spinlock_t *lock);
clk_register_composite
顾名思义, 就是mux、divider、gate等clock的组合, 可通过下面接口注册:
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-composite.c
/***
* struct clk_composite - aggregate clock of mux, divider and gate clocks
*
* @hw: handle between common and hardware-specific interfaces
* @mux_hw: handle between composite and hardware-specific mux clock
* @rate_hw: handle between composite and hardware-specific rate clock
* @gate_hw: handle between composite and hardware-specific gate clock
* @mux_ops: clock ops for mux
* @rate_ops: clock ops for rate
* @gate_ops: clock ops for gate
*/
struct clk_composite {
struct clk_hw hw;
struct clk_ops ops;
struct clk_hw *mux_hw;
struct clk_hw *rate_hw;
struct clk_hw *gate_hw;
const struct clk_ops *mux_ops;
const struct clk_ops *rate_ops;
const struct clk_ops *gate_ops;
};
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);
看着有点复杂, 但理解了上面1~5类clock, 这里就只剩下苦力了, 耐心一点,就可以了.
另外, clock子系统没有提供相关的DTS解析函数.
clk_register_gpio_gate
把某个gpio当做一个gate clock. 也就是说可以通过clock子系统来控制这个gpio 开/关, 也就是控制这个GPIO输出高/低电平.
某些情况下, 有的模块需要通过某个GPIO来控制其使能/禁止, 例如蓝牙模块. 而且你也不需要向此模块提供时钟, 模板本身就有晶振存在, 可以自己给自己供时钟.
这个时候我们就可以把该GPIO抽象成gpio gate clock, 借助clock子系统, 来控制模块的enable/disable.
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk-gpio-gate.c
/***
* struct clk_gpio_gate - gpio gated clock
*
* @hw: handle between common and hardware-specific interfaces
* @gpiod: gpio descriptor
*
* Clock with a gpio control for enabling and disabling the parent clock.
* Implements .enable, .disable and .is_enabled
*/
struct clk_gpio {
struct clk_hw hw;
struct gpio_desc *gpiod;
};
extern const struct clk_ops clk_gpio_gate_ops;
struct clk *clk_register_gpio_gate(struct device *dev, const char *name,
const char *parent_name, unsigned gpio, bool active_low,
unsigned long flags);
void of_gpio_clk_gate_setup(struct device_node *node);
API 参数比较简单, 不多说了.
另外, clock子系统还提供了此种类型clock的DTS接口.
xxx_unregister
上述xxx_register函数都有对应的xxx_unregister.
unregister的函数原型在上述代码中都有提及, 这里不多说了, 有兴趣可以自行阅读源代码.
clk_register_clkdev
头文件: include/linux/clkdev.h
实现文件: drivers/clk/clkdev.c
原型: int clk_register_clkdev(struct clk *, const char *, const char *, ...);
上述的clk_register_xxx接口会向clock子系统注册一个clock, 并返回一个代表该clock的struct clk结构体.
clk_register_clkdev的作用就是把返回的这个struct clk添加到某个池子里面.
这个池子其实就是个链表啦, 链表定义在clkdev.c里面.
池子里面主要是用name做为关键字, 区分不同的clk.
当consumer端想要查询某个clk的时候, 就会尝试从这个链表里面通过clock name去检索.
of_clk_add_provider
头文件: include/linux/clk-provider.h
实现文件: drivers/clk/clk.c
原型:
int of_clk_add_provider(struct device_node *np,
struct clk *(*clk_src_get)(struct of_phandle_args *args,
void *data),
void *data);
此API的主要目的也是把得到的struct clk结构体添加到某个池子里面.
这个池子也是个链表, 定义在clk.c里面.
与上面那个池子的不同之处在于, 这里是用device_node做为关键字来区分不同的clk.
当consumer端想要查询某个clk的时候, 会在DTS node里面通过clocks = <&xxxx>来引用某个clock.
通过引用的这个clock的phandle, 就能找到对应的device_node. 然后通过device_node就能从池子里面检索出需要的clk.
其实, 这两种池子对应了我们的consumer端的两种方式: 一种是通过name获取clk, 不需要DTS; 另外一种就是通过DTS.
随着Linux内核大力推行DTS, 方式二会逐渐成为主流.
3. clock core -- 如何管理Clocks
4.1 简介
第3章我们描述了clock子系统的功能之一 : 向底层的clock driver提供注册接口.
本章我们描述clock子系统的功能之二 : 如何管理这些clocks.
当clock driver向子系统注册某一个clock的时候, 子系统内部会创建一个数据结构来表示这个clock. 这个数据结构在前文提过, 就是struct clk.
一个struct clk就对应一个clock. 到目前为止, 我们还没见过这个数据结构的庐山真面目呢! 本章会围绕这个数据结构展开, 充分理解它, 你就里面本章了.
4.2 主要数据结构
struct clk
结构体定义在一个C文件里面 : drivers/clk/clk.c
struct clk |
Comment |
struct clk_core*core |
clk_core是clock核心层的一个私有数据结构, 下面细述 |
const char *dev_id |
使用该clk的device的name |
const char *con_id |
connection ID string on device |
unsigned long min_rate |
最小rate |
unsigned long max_rate |
最大rate |
struct hlist_node clks_node |
这个结构体有点颠覆本文的一个观点, 那就是: 一个struct clk结构体对应一个具体的clock.
仔细阅读源码后发现, 原来每当某一个driver尝试获取某个clock的时候, clock子系统就会创建一个struct clk. 例如假设有个clock, 名称是pll_clk. GPIO, SPI, I2C这3个模块都是用此pll_clk做为工作时钟的. 那么clock子系统核心层会有一个clk_core用于描述pll_clk, 每当GPIO/SPI/I2C的driver首次尝试获取pll_clk时, clock子系统就会创建一个struct clk结构体, 并将创建的这个struct clk返回给对应的driver. 该clk结构体的dev_id和con_id都是对应的driver指定的.
不过为了简便起见, 我们在本文中还是暂定一个struct clk对应一个clock吧, 理解起来不会有什么异常. |
struct clk_core
前文我们说过, 一个struct clk就代表一个clock. 一个clk_core与clock也是一一对应的关系. 那它俩有什么区别呢?
struct clk更像是对外的接口, 例如当provider向clock子系统注册时, 它会得到一个struct clk*的返回结果; 当consumer想要使用某个clock时, 也会首先获取struct clk*这个结构体.
struct clk_core则是clock子系统核心层的一个私有数据结构, 在核心层代描述某个具体的clock. provider或consumer不会触碰这个数据结构.
结构体定义在一个C文件里面 : drivers/clk/clk.c
struct clk_core |
Comment |
const char*name |
此clock的name, provider在注册的时候会提供clock的name |
const struct clk_ops*ops |
操作此clock的ops, provider在注册的时候会提供clock的ops |
struct clk_hw*hw |
provider端提供的clk_hw结构体 |
struct module*owner |
|
struct clk_core*parent |
一个clock可能有多个parent, 但是同一时刻只能有一个parent有效. 这里指向有效的那个父时钟所对应的clk_core |
const char**parent_names |
所有可选的父时钟的names |
struct clk_core**parents |
所有可选的父时钟的clk_core |
u8num_parents |
有多少个parents |
u8new_parent_index |
你可以把所有的parents理解成一个数组, index就对应数组的某个元素 |
unsigned longrate |
clock的rate |
unsigned longreq_rate |
consumer端要求的rate |
unsigned longnew_rate |
新的rate |
struct clk_core*new_parent |
新的parent |
struct clk_core*new_child |
新的child |
unsigned longflags |
此clock的flags, 可选的flag在3.3节《struct clk_init_data》中有介绍 |
unsigned intenable_count |
被使能的次数 |
unsigned intprepare_count |
被prepare的次数 |
unsigned longaccuracy |
此clock当前的accuracy, The clock accuracy is expressed in ppb (parts per billion) |
intphase |
此clock当前的phase, 取值范围 0 - 359 |
struct hlist_headchildren |
链表头, 用于挂接本clock所有的children |
struct hlist_nodechild_node |
链表节点, 用于把本clock挂接到对应的parent的children链表下 |
struct hlist_nodedebug_node |
链表节点, 与debugfs相关 |
struct hlist_headclks |
一个struct clk_core可能对应多个struct clk, 这个链表头挂接所有的clks. 关于struct clk_core和struct clk的关系, 参见4.2节《struct clk》的说明 |
unsigned intnotifier_count |
内核通知链机制相关. 当内核其它模块想知道某个clock的变化时 (例如频率等的改变), 可以向此clock的通知链注册. 这样当clock发生变化时, 就会向通知链上的所有模块发送通知 notifier_count代表有多少个模块向本clock注册了 |
struct dentry*dentry |
debugfs相关 |
struct krefref |
引用计数 |
4.3 关键代码分析
clk_register
我们在3.4节介绍了clock子系统提供给provider端的总多clk_register_xxx函数. 这些函数都是对clk_register的封装, 基础都是clk_register.
因此我们本章只重点讲解这一个API, 其它API有兴趣可以自己阅读源码.
在开始分析之前, 结合之前讲解的内容, 你能猜到clk_register会做哪些事情吗?
首先, 它得创建struct clk和struct clk_core这两个数据结构.
然后, 它得维护clk_core的树形关系, 就像时钟数的硬件形态那样:
有一个或多个ROOT_CLOCK, ROOT_CLOCK没有parent, 下面挂载的是children, children下面在挂载children.
为什么要维护这样的树形结构呢? 因为我们在操作某个clk时, 往往会跟它的parent有关: 例如要使能某个clk, 必须保证它的parent也使能; 或者parent的rate改变了, 那它的children的rate都有可能改变. 因此clock子系统必须维护好树形结构, 才能方便的处理这些相关性.
下面我们来看看代码:
实现文件: drivers/clk/clk.c
/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the primary interface for populating the clock tree with new
* clock nodes. It returns a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjuction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
int i, ret;
struct clk_core *clk;
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk) {
pr_err("%s: could not allocate clk\n", __func__);
ret = -ENOMEM;
goto fail_out;
}
clk->name = kstrdup_const(hw->init->name, GFP_KERNEL);
if (!clk->name) {
pr_err("%s: could not allocate clk->name\n", __func__);
ret = -ENOMEM;
goto fail_name;
}
clk->ops = hw->init->ops;
if (dev && dev->driver)
clk->owner = dev->driver->owner;
clk->hw = hw;
clk->flags = hw->init->flags;
clk->num_parents = hw->init->num_parents;
hw->core = clk;
/* allocate local copy in case parent_names is __initdata */
clk->parent_names = kcalloc(clk->num_parents, sizeof(char *),
GFP_KERNEL);
if (!clk->parent_names) {
pr_err("%s: could not allocate clk->parent_names\n", __func__);
ret = -ENOMEM;
goto fail_parent_names;
}
/* copy each string name in case parent_names is __initdata */
for (i = 0; i < clk->num_parents; i++) {
clk->parent_names[i] = kstrdup_const(hw->init->parent_names[i],
GFP_KERNEL);
if (!clk->parent_names[i]) {
pr_err("%s: could not copy parent_names\n", __func__);
ret = -ENOMEM;
goto fail_parent_names_copy;
}
}
INIT_HLIST_HEAD(&clk->clks);
hw->clk = __clk_create_clk(hw, NULL, NULL);
if (IS_ERR(hw->clk)) {
pr_err("%s: could not allocate per-user clk\n", __func__);
ret = PTR_ERR(hw->clk);
goto fail_parent_names_copy;
}
ret = __clk_init(dev, hw->clk);
if (!ret)
return hw->clk;
__clk_free_clk(hw->clk);
hw->clk = NULL;
fail_parent_names_copy:
while (--i >= 0)
kfree_const(clk->parent_names[i]);
kfree(clk->parent_names);
fail_parent_names:
kfree_const(clk->name);
fail_name:
kfree(clk);
fail_out:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(clk_register);
上面这段代码的逻辑比较简单:
首先, 创建了clk_core结构体, 然后用provider提供的clk_hw, 填充clk_core中的各个字段.
然后, 调用 __clk_create_clk, 在__clk_create_clk里面会创建struct clk这个结构体并初始化其相关字段. clk_register会返回给provider一个struct clk*的结构体指针, 这个clk*就是这里创建的.
下文我们会单独介绍__clk_create_clk这个函数.
最后, 会调用__clk_init, 在__clk_init里面会处理clk_core之间的树形结构关系.
下文我们会单独介绍__clk_init这个函数.
__clk_create_clk
这个API会创建一个struct clk结构体.
要理解此API的细节, 首先得理清楚struct clk和struct clk_core的关系.
我们在4.2节《struct clk_core》中已经初步介绍了clk和clk_core的关系, 可以回头看看.
这里我们在做进一步的说明.
还记得我们在《字符设备驱动》一文中介绍过的struct file和struct inode这两个结构体吗? inode在物理上代表一个文件, 一个文件对应唯一一个inode; file则代表一个打开的文件, 文件被打开几次, 就会有几个file.
clk和clk_core的关系与上面很类型, clk_core代表一个硬件上的clock, 一个clock对应唯一一个clk_core; 而clk则代表被使用的clock, 例如GPIO driver想要获取某个clock, 它会得到一个struct clk, SPI driver想要获取同一个clock, 它也会得到一个struct clk, 另外, 当此clock的provider向clock子系统注册时, provider也会得到一个struct clk.
接下来我们看看代码细节吧:
struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
const char *con_id)
{
struct clk *clk;
/* This is to allow this function to be chained to others */
if (!hw || IS_ERR(hw))
return (struct clk *) hw;
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk)
return ERR_PTR(-ENOMEM);
clk->core = hw->core;
clk->dev_id = dev_id;
clk->con_id = con_id;
clk->max_rate = ULONG_MAX;
clk_prepare_lock();
hlist_add_head(&clk->clks_node, &hw->core->clks);
clk_prepare_unlock();
return clk;
}
逻辑比较简单:
首先创建struct clk结构体, 然后初始化结构体的相关参数, 最后把这个struct clk挂载到clk_core的clks链表头下面.
__clk_init
这个API主要是处理clock之间的树形关系.
当任何一个clock调用clk_register接口进行注册时, __clk_init都会负责把这个clock放在树形结构的恰当位置.
代码细节如下:
1: /**
2: * __clk_init - initialize the data structures in a struct clk
3: * @dev: device initializing this clk, placeholder for now
4: * @clk: clk being initialized
5: *
6: * Initializes the lists in struct clk, queries the hardware for the
7: * parent and rate and sets them both.
8: */
9: int __clk_init(struct device *dev, struct clk *clk)
10: {
11: int i, ret = 0;
12: struct clk *orphan;
13: struct hlist_node *tmp2;
14:
15: if (!clk)
16: return -EINVAL;
17:
18: clk_prepare_lock();
19:
20: /* check to see if a clock with this name is already registered */
21: if (__clk_lookup(clk->name)) {
22: pr_debug("%s: clk %s already initialized\n",
23: __func__, clk->name);
24: ret = -EEXIST;
25: goto out;
26: }
27:
28: /* check that clk_ops are sane. See Documentation/clk.txt */
29: if (clk->ops->set_rate &&
30: !(clk->ops->round_rate && clk->ops->recalc_rate)) {
31: pr_warning("%s: %s must implement .round_rate & .recalc_rate\n",
32: __func__, clk->name);
33: ret = -EINVAL;
34: goto out;
35: }
36:
37: if (clk->ops->set_parent && !clk->ops->get_parent) {
38: pr_warning("%s: %s must implement .get_parent & .set_parent\n",
39: __func__, clk->name);
40: ret = -EINVAL;
41: goto out;
42: }
43:
44: /* throw a WARN if any entries in parent_names are NULL */
45: for (i = 0; i < clk->num_parents; i++)
46: WARN(!clk->parent_names[i],
47: "%s: invalid NULL in %s's .parent_names\n",
48: __func__, clk->name);
49:
50: /*
51: * Allocate an array of struct clk *'s to avoid unnecessary string
52: * look-ups of clk's possible parents. This can fail for clocks passed
53: * in to clk_init during early boot; thus any access to clk->parents[]
54: * must always check for a NULL pointer and try to populate it if
55: * necessary.
56: *
57: * If clk->parents is not NULL we skip this entire block. This allows
58: * for clock drivers to statically initialize clk->parents.
59: */
60: if (clk->num_parents > 1 && !clk->parents) {
61: clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
62: GFP_KERNEL);
63: /*
64: * __clk_lookup returns NULL for parents that have not been
65: * clk_init'd; thus any access to clk->parents[] must check
66: * for a NULL pointer. We can always perform lazy lookups for
67: * missing parents later on.
68: */
69: if (clk->parents)
70: for (i = 0; i < clk->num_parents; i++)
71: clk->parents[i] =
72: __clk_lookup(clk->parent_names[i]);
73: }
74:
75: clk->parent = __clk_init_parent(clk);
76:
77: /*
78: * Populate clk->parent if parent has already been __clk_init'd. If
79: * parent has not yet been __clk_init'd then place clk in the orphan
80: * list. If clk has set the CLK_IS_ROOT flag then place it in the root
81: * clk list.
82: *
83: * Every time a new clk is clk_init'd then we walk the list of orphan
84: * clocks and re-parent any that are children of the clock currently
85: * being clk_init'd.
86: */
87: if (clk->parent)
88: hlist_add_head(&clk->child_node,
89: &clk->parent->children);
90: else if (clk->flags & CLK_IS_ROOT)
91: hlist_add_head(&clk->child_node, &clk_root_list);
92: else
93: hlist_add_head(&clk->child_node, &clk_orphan_list);
94:
95: /*
96: * Set clk's rate. The preferred method is to use .recalc_rate. For
97: * simple clocks and lazy developers the default fallback is to use the
98: * parent's rate. If a clock doesn't have a parent (or is orphaned)
99: * then rate is set to zero.
100: */
101: if (clk->ops->recalc_rate)
102: clk->rate = clk->ops->recalc_rate(clk->hw,
103: __clk_get_rate(clk->parent));
104: else if (clk->parent)
105: clk->rate = clk->parent->rate;
106: else
107: clk->rate = 0;
108:
109: /*
110: * walk the list of orphan clocks and reparent any that are children of
111: * this clock
112: */
113: hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
114: if (orphan->ops->get_parent) {
115: i = orphan->ops->get_parent(orphan->hw);
116: if (!strcmp(clk->name, orphan->parent_names[i]))
117: __clk_reparent(orphan, clk);
118: continue;
119: }
120:
121: for (i = 0; i < orphan->num_parents; i++)
122: if (!strcmp(clk->name, orphan->parent_names[i])) {
123: __clk_reparent(orphan, clk);
124: break;
125: }
126: }
127:
128: /*
129: * optional platform-specific magic
130: *
131: * The .init callback is not used by any of the basic clock types, but
132: * exists for weird hardware that must perform initialization magic.
133: * Please consider other ways of solving initialization problems before
134: * using this callback, as it's use is discouraged.
135: */
136: if (clk->ops->init)
137: clk->ops->init(clk->hw);
138:
139: clk_debug_register(clk);
140:
141: out:
142: clk_prepare_unlock();
143:
144: return ret;
145: }
这一段代码的逻辑很复杂, 主要做的事情如下:
20~26行,以clock name为参数,调用__clk_lookup接口,查找是否已有相同name的clock注册,如果有,则返回错误。由此可以看出,clock framework以name唯一识别一个clock,因此不能有同名的clock存在
28~42行,检查clk ops的完整性,例如:如果提供了set_rate接口,就必须提供round_rate和recalc_rate接口;如果提供了set_parent,就必须提供get_parent
50~73行,分配一个struct clk *类型的数组,缓存该clock的parents clock。具体方法是根据parents_name,查找相应的struct clk指针
75行,获取当前的parent clock,并将其保存在parent指针中。具体可参考下面“说明2”
77~93行,根据该clock的特性,将它添加到clk_root_list、clk_orphan_list或者parent->children三个链表中的一个,具体请参考下面“说明1”
95~107行,计算clock的初始rate,具体请参考下面“说明3”
109~126行,尝试reparent当前所有的孤儿(orphan)clock,具体请参考下面“说明4”
128~137行,如果clock ops提供了init接口,执行之(由注释可知,kernel不建议提供init接口)
说明1: clock的管理和查询
clock framework有2条全局的链表:clk_root_list和clk_orphan_list。所有设置了CLK_IS_ROOT属性的clock都会挂在clk_root_list中。其它clock,如果有valid的parent ,则会挂到parent的“children”链表中,如果没有valid的parent,则会挂到clk_orphan_list中。
查询时(__clk_lookup接口做的事情),依次搜索:
clk_root_list-->root_clk-->children-->child's children
clk_orphan_list-->orphan_clk-->children-->child's children
即可
说明2:当前parent clock的选择(__clk_init_parent)
对于没有parent,或者只有1个parent 的clock来说,比较简单,设置为NULL,或者根据parent name获得parent的struct clk指针接。
对于有多个parent的clock,就必须提供.get_parent ops,该ops要根据当前硬件的配置情况,例如寄存器值,返回当前所有使用的parent的index(即第几个parent)。然后根据index,取出对应parent clock的struct clk指针,作为当前的parent。
说明3: clock的初始rate计算
对于提供.recalc_rate ops的clock来说,优先使用该ops获取初始的rate。如果没有提供,退而求其次,直接使用parent clock的rate。最后,如果该clock没有parent,则初始的rate只能选择为0
.recalc_rate ops的功能,是以parent clock的rate为输入参数,根据当前硬件的配置情况,如寄存器值,计算获得自身的rate值
说明4:orphan clocks的reparent
有些情况下,child clock会先于parent clock注册,此时该child就会成为orphan clock,被收养在clk_orphan_list中。
而每当新的clock注册时,kernel都会检查这个clock是否是某个orphan的parent,如果是,就把这个orphan从clk_orphan_list中移除,放到新注册的clock的怀抱。这就是reparent的功能,它的处理逻辑是:
遍历orphan list,如果orphan提供了.get_parent ops,则通过该ops得到当前parent的index,并从parent_names中取出该parent的name,然后和新注册的clock name比较,如果相同,呵呵,找到parent了,执行__clk_reparent,进行后续的操作
如果没有提供.get_parent ops,只能遍历自己的parent_names,检查是否有和新注册clock匹配的,如果有,执行__clk_reparent,进行后续的操作
__clk_reparent会把这个orphan从clk_orphan_list中移除,并挂到新注册的clock上。然后调用__clk_recalc_rates,重新计算自己以及自己所有children的rate。计算过程和上面的clock rate设置类似
4. clock consumer -- 如何使用Clocks
5.1 简介
clock 子系统管理clocks的最终目的, 是让device driver可以方便的获取并使用这些clocks.
我们知道clock子系统用一个struct clk结构体来抽象某一个clock.
当device driver要操作某个clock时, 它需要做两件事情:
首先, 获取clock. 也叫clk_get.
然后, 操作这个clock. 如 clk_prepare/ clk_enable/ clk_disable/ clk_set_rate/ …
举个例子, 以GPIO控制器为例. 从硬件的角度来说, GPIO控制器需要工作时钟. 因此, 在GPIO控制器的platform_driver的probe函数里面, 就需要操作这个时钟.
操作的第一步是获取clock, 获取clock有两种方式:
第一种方式是直接通过name来获取, 例如假设已知GPIO控制器的工作时钟的name是”gpio_clk”, 那么我们就可以在probe函数里面通过clk_get(&device, “gpio_clk”) 这种方式获取.
另外一种方式是说, 对于GPIO控制器来讲, 我们会有一个platform_device, 在里面描述GPIO控制器的资源, 也就是寄存器地址, 中断号什么的. GPIO控制器的工作时钟也算一种资源了, 我们能否在platform_device里面一并描述这个资源呢?
答案是可以. platform_device现在都是在DTS中描述的, 因此这个GPIO控制器的DTS可以这样写:
gpio : gpio-controller@xxxx {
compatible = “yyyy”;
reg = <…. ….>;
……
clocks = <&theclock>; /* 指明/引用某一个clock */
}
我们用clocks这个property来描述时钟资源.
在对应的platform_driver的probe函数里面, 我们就可以通过clk_get(&device, NULL)这种方式来获取所需的clock.
这种方式下, 我们只需要知道platform_device, 然后就能通过它找到对应的DTS node, 然后就能通过DTS node的”clocks” property获取到对应的时钟资源.
获取到clk之后, 就可以通过clock子系统提供的API来操作clk了.
clock子系统提供给consumer端的APIs都定义在头文件 include/linux/clk.h里面.
接下来的章节, 我们会分两部分来介绍这些APIs.
5.2节介绍获取clk相关的APIs.
5.3节介绍操作clk相关的APIs.
5.2 APIs – 获取clk
头文件: include/linux/clk.h
实现文件: drivers/clk/clkdev.c
最主要的两个API:
struct clk *clk_get(struct device *dev, const char *id);
struct clk *devm_clk_get(struct device *dev, const char *id);
devm_clk_get是devm版本的clk_get. 用于自动释放资源, 我们在《设备模型》一文中有过介绍, 这里不多说了.
除了这两个用的最多的API之外, 还有一些其他的API, 如下:
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
struct clk *of_clk_get(struct device_node *np, int index);
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec);
clk_get相当于一个总逻辑, 它会根据不同的情况调用上述这些API, 在实际代码中, 基本上我们只会用到clk_get或者devm_clk_get.
接下来, 我们会仔细看看clk_get的内部逻辑.
clk_get / devm_clk_get
头文件: include/linux/clk.h
实现文件: drivers/clk/clkdev.c
原型: struct clk *clk_get(struct device *dev, const char *id);
该API的主要作用就是根据参数, 从clock子系统中获取一个clk给到consumer, 然后consumer就可以操作该clk了. 因此该API的返回值就是用于描述某个clock的数据结构: struct clk.
代码细节如下:
struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
struct clk *clk;
if (dev) {
clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
return clk;
}
return clk_get_sys(dev_id, con_id);
}
EXPORT_SYMBOL(clk_get);
如果你已经充分理解了2.3节的那个系统框图, 那么此处的逻辑就很简单了:
如果dev不为空, 那么就以dev->of_node为参数, 调用of_XXX那一套, 从LIST_HEAD(of_clk_providers)这个池子里面查询某个clk.
如果查询到了, 则返回该clk. 这种情况其实对应5.1节中描述的DTS node方式.
如果上面没有获取到clk, 则调用clk_get_sys, 从LIST_HEAD(clocks)这个池子里面查询clk. 查询的关键字是con_id, 其实就是clock的name.
5.3 APIs – 操作clk
头文件: include/linux/clk.h
实现文件: drivers/clk/clk.c
1: int clk_prepare(struct clk *clk)
2: void clk_unprepare(struct clk *clk)
3:
4: static inline int clk_enable(struct clk *clk)
5: static inline void clk_disable(struct clk *clk)
6:
7: static inline unsigned long clk_get_rate(struct clk *clk)
8: static inline int clk_set_rate(struct clk *clk, unsigned long rate)
9: static inline long clk_round_rate(struct clk *clk, unsigned long rate)
10:
11: static inline int clk_set_parent(struct clk *clk, struct clk *parent)
12: static inline struct clk *clk_get_parent(struct clk *clk)
13:
14: static inline int clk_prepare_enable(struct clk *clk)
15: static inline void clk_disable_unprepare(struct clk *clk)
clk_enable/clk_disable: 启动/停止clock. 不会睡眠
clk_prepare/clk_unprepare: 启动clock前的准备工作/停止clock后的善后工作. 可能会睡眠
clk_get_rate/clk_set_rate/clk_round_rate: clock频率的获取和设置, 其中clk_set_rate可能会不成功(例如没有对应的分频比), 此时会返回错误. 如果要确保设置成功, 则需要先调用clk_round_rate接口, 得到和需要设置的rate比较接近的那个值
clk_set_parent / clk_get_parent: 获取/选择clock的parent clock
clk_prepare_enable: 将clk_prepare和clk_enable组合起来,一起调用
clk_disable_unprepare: 将clk_disable和clk_unprepare组合起来,一起调用
prepare/unprepare,enable/disable的说明:
这两套API的本质,是把clock的启动/停止分为atomic和non-atomic两个阶段,以方便实现和调用。
因此上面所说的“不会睡眠/可能会睡眠”,有两个角度的含义:
一是告诉底层的clock driver,请把可能引起睡眠的操作,放到prepare/unprepare中实现,一定不能放到enable/disable中
二是提醒上层使用clock的driver,调用prepare/unprepare接口时可能会睡眠哦,千万不能在atomic上下文(例如中断处理中)调用哦,而调用enable/disable接口则可放心。
另外,clock的开关为什么需要睡眠呢?这里举个例子,例如enable PLL clk,在启动PLL后,需要等待它稳定。而PLL的稳定时间是很长的,这段时间要把CPU交出(进程睡眠),不然就会浪费CPU。
最后,为什么会有合在一起的clk_prepare_enable/clk_disable_unprepare接口呢?如果调用者能确保是在non-atomic上下文中调用,就可以顺序调用prepare/enable、disable/unprepared,为了简单,framework就帮忙封装了这两个接口。
5.4 其它APIs
头文件: include/linux/clk.h
实现文件: drivers/clk/clk.c
1: int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
2: int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
这两个API与内核提供的通知链机制有关.
这两个notify接口, 用于注册/注销 clock rate改变的通知.
例如某个driver关心某个clock, 期望这个clock的rate改变时, 通知到自己, 就可以注册一个notify.