3.8.1. Clock 驱动
本章节描述 ZX 平台的 U-Boot 时钟配置相关内容。
3.8.1.1. 驱动框架
U-Boot 驱动模型支持 Clock,ZX 平台中 Clock 驱动基于该框架进行实现。 相关配置为:
CONFIG_CLK
CONFIG_CLK_ZX
CONFIG_CLK_ZX_CMU
CONFIG_SPL_CLK_ZX
CONFIG_SPL_CLK_ZX_CMU
相关源码有:
include/clk.h
include/clk-uclass.h
drivers/clk/clk.c
drivers/clk/clk-uclass.c
drivers/clk/zx/clk-aic.h
drivers/clk/zx/clk-zx.c
drivers/clk/zx/clk-cmu.c
3.8.1.2. 驱动接口
相关的 Clock 驱动接口有:
int clk_get_by_index_platdata(struct udevice *dev, int index,
struct phandle_1_arg *cells, struct clk *clk);
int clk_get_by_index(struct udevice *dev, int index, struct clk *clk);
int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk);
int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk);
int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk);
int clk_release_all(struct clk *clk, int count);
int clk_enable(struct clk *clk);
int clk_disable(struct clk *clk);
ulong clk_set_rate(struct clk *clk, ulong rate);
ulong clk_get_rate(struct clk *clk);
3.8.1.3. 初始化和使用
通常硬件设备初始化时,需要配置对应的时钟。Clock 驱动的 probe 在时钟设备第一次被获取时触发。
clk_get_by_index(); // drivers/clk/clk-uclass.c
|-> clk_get_by_index_tail();
|-> uclass_get_device_by_ofnode(UCLASS_CLK, args->node, &dev_clk);
|-> uclass_find_device_by_ofnode(id, node, &dev);
|-> uclass_get_device_tail(dev, ret, devp); // drivers/core/uclass.c
|-> device_probe(dev); // drivers/core/device.c
|-> drv->probe(dev);
aic_clk_probe(dev);
// drivers/clk/zx/clk-cmu.c
设备使用的时钟通过时钟树进行管理。在时钟树中,每一个时钟都被分配一个具体的 ID, 并且在 DTS 中配置给需要的硬件设备。设备初始化时,通过 FDT 获取对应的时钟设备。
DTS 中时钟配置示例:
dma: dma-controller@10000000 {
compatible = "zx,aic-dma";
...
clocks = <&ccu CLK_DMA>;
...
};
相关 ID 定义可参考:
include/dt-bindings/clock/zx,aic-cmu.h
获取时钟设备的流程:
clk_get_by_index(dev, index, clk); // drivers/clk/clk-uclass.c
| // 此处 index 是 DTS 中配置给该设备的第几个时钟
|
|-> clk_get_by_index_tail();
|-> uclass_get_device_by_ofnode(UCLASS_CLK, args->node, &dev_clk);
|-> clk_of_xlate_default(clk, args);
|-> clk->id = args->args[0]; // 获取到具体的时钟 ID
需要设置和获取相关时钟信息时,通过 clk->id
访问时钟树。
clk_set_rate(clk, rate); // drivers/clk/clk-uclass.c
|-> ops->set_rate(clk, rate);
zx_clk_set_rate(clk, rate); // drivers/clk/zx/clk-zx.c
|-> aic_get_clk_info(priv->tree, clk->id, &index);
// 驱动内部,使用 clk-id 获取对应的时钟节点