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 获取对应的时钟节点