目录

1. clock provider的DTS

1.1 方式1

1.2 方式2 

2 clock consumer的DTS

        上文已经介绍了一些provider和consumer的DTS,本文具体介绍一下。

1. clock provider的DTS

        我们知道,DTS(Device Tree Source)是用来描述设备信息的,那系统的clock资源,是什么设备呢?换句话,用什么设备表示呢?这决定了clock provider的DTS怎么写。

        通常有两种方式:

1.1 方式1

        将系统所有的clock,抽象为一个虚拟的设备,用一个DTS node表示。这个虚拟的设备称作clock controller,参考如下例子:

/* arch/arm/boot/dts/exynos4210.dtsi */
clock: clock-controller@0x10030000 {
        compatible = "samsung,exynos4210-clock";
        reg = <0x10030000 0x20000>;
        #clock-cells = <1>;
};

clock:该clock设备的名称,clock consumer可以根据该名称引用clock;
#clock-cells:该clock的cells,1表示该clock有多个输出,clock consumer需要通过ID值指定所要使用的clock(很好理解,系统那么多clock,被抽象为1个设备,因而需要额外的ID标识)。

1.2 方式2 

        每一个可输出clock的器件,如上文所提及的Oscillator、PLL、Mux等等,都是一个设备,用一个DTS node表示。每一个器件,即是clock provider,也是clock consumer(根节点除外,如OSC),因为它需要接受clock输入,经过处理后,输出clock。参考如下例子(如果能拿到对应的datasheet会更容易理解):

/* arch/arm/boot/dts/sun4i-a10.dtsi */
clocks {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;
 
    osc24M: osc24M@01c20050 {
        #clock-cells = <0>;
        compatible = "allwinner,sun4i-osc-clk";
        reg = <0x01c20050 0x4>;
        clock-frequency = <24000000>;
    };
 
    osc32k: osc32k {
        #clock-cells = <0>;
        compatible = "fixed-clock";
        clock-frequency = <32768>;
    };
 
    pll1: pll1@01c20000 {
        #clock-cells = <0>;
        compatible = "allwinner,sun4i-pll1-clk";
        reg = <0x01c20000 0x4>;
        clocks = <&osc24M>;
    };
 
    ahb_gates: ahb_gates@01c20060 {
        #clock-cells = <1>;
        compatible = "allwinner,sun4i-ahb-gates-clk";
        reg = <0x01c20060 0x8>;
        clocks = <&ahb>;
        clock-output-names = "ahb_usb0", "ahb_ehci0",
            "ahb_ohci0", "ahb_ehci1", "ahb_ohci1", "ahb_ss",
            "ahb_dma", "ahb_bist", "ahb_mmc0", "ahb_mmc1",
            "ahb_mmc2", "ahb_mmc3", "ahb_ms", "ahb_nand",
            "ahb_sdram", "ahb_ace",    "ahb_emac", "ahb_ts",
            "ahb_spi0", "ahb_spi1", "ahb_spi2", "ahb_spi3",
            "ahb_pata", "ahb_sata", "ahb_gps", "ahb_ve",
            "ahb_tvd", "ahb_tve0", "ahb_tve1", "ahb_lcd0",
            "ahb_lcd1", "ahb_csi0", "ahb_csi1", "ahb_hdmi",
            "ahb_de_be0", "ahb_de_be1", "ahb_de_fe0",
            "ahb_de_fe1", "ahb_mp", "ahb_mali400";
    };
};

        (1)osc24M和osc32k是两个root clock,因此只做clock provider功能。它们的cells均为0,因为直接使用名字即可引用。另外,增加了“clock-frequency”自定义关键字,这样在板子使用的OSC频率改变时,如变为12M,不需要重新编译代码,只需更改DTS的频率即可(这不正是Device Tree的核心思想吗!)。话说回来了,osc24M的命名不是很好,如果频率改变,名称也得改吧,clock consumer的引用也得改吧;

       (2)pll1即是clock provider(cell为0,直接用名字引用),也是clock consumer(clocks关键字,指定输入clock为“osc24M”);

       (3)再看一个复杂一点的,ahb_gates,它是clock provider(cell为1),通过clock-output-names关键字,描述所有的输出时钟。同时它也是clock consumer(由clocks关键字可知输入clock为“ahb”)。需要注意的是,clock-output-names关键字只为了方便clock provider编程方便(后面会讲),clock consumer不能使用(或者可理解为不可见);

2 clock consumer的DTS

        在1.2的方法二,我们已经看到clock consumer的DTS了,因为很多clock provider也是clock consumer。这里再举几个例子,做进一步说明。

例子1(对应1.1的方式1,来自同一个DTS文件):

/* arch/arm/boot/dts/exynos4210.dtsi */
mct@10050000 {
        compatible = "samsung,exynos4210-mct";
        ...
        clocks = <&clock 3>, <&clock 344>;
        clock-names = "fin_pll", "mct";
        ...
};

        (1)clocks,指明该设备的clock列表,clk_get时,会以它为关键字,去device_node中搜索,以得到对应的struct clk指针;

         clocks需要指明的信息,由clock provider的“#clock-cells”规定:为0时,只需要提供一个clock provider name(称作phandle);为1时,表示phandle有多个输出,则需要额外提供一个ID,指明具体需要使用那个输出。这个例子直接用立即数表示,更好的做法是,将系统所有clock的ID,定义在一个头文件中,而DTS可以包含这个头文件,如“clocks = <&clock CLK_SPI0>”;

        (2)clock-names,为clocks指定的那些clock分配一些易于使用的名字,driver可以直接以名字为参数,get clock的句柄。

例子2,如果clock provider的“#clock-cells”为0,可直接引用该clock provider的名字,具体可参考1.2的方式2。

例子3,1.2中方式2有一个clock provider的名字为apb0_gates,它的“#clock-cells”为1,并通过clock-output-names指定了所有的输出clock,那么,clock consumer怎么引用呢?

/* arch/arm/boot/dts/sun4i-a10.dtsi */
soc@01c20000 {
        compatible = "simple-bus";
        ...
 
        pio: pinctrl@01c20800 {
                compatible = "allwinner,sun4i-a10-pinctrl";
                reg = <0x01c20800 0x400>;
                clocks = <&apb0_gates 5>;
                ...
        }
}

        和例子1一样,指定phandle为“aph0_gates”,ID为5。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐