• 设备树中时钟


    时钟框图

    先来看看S3C2440时钟的硬件框图:

    将该图简化如下:

     我们只想作为消费者怎么去使用这些时钟,并不关心“提供者”内部的层级结构,只要知道“直接提供者”,也不关系“直接提供者”的实现,我们只需要发出请求就可以了。

    晶振设备树描述

    我们看看在2440的设备树里怎么描述这提供者和消费者。先来看看晶振:

        xti: xti_clock {
            compatible = "fixed-clock";
            clock-frequency = <12000000>;
            clock-output-names = "xti";
            #clock-cells = <0>;
        };

    根据compatible可以找到对应的驱动,驱动程序将晶振的频率记录下来,以后作为计算的基准。

    然后再是PLL的设备节点:

        clocks: clock-controller@4c000000 {
            compatible = "samsung,s3c2440-clock";
            reg = <0x4c000000 0x20>;
            #clock-cells = <1>;
        };

    设备节点本身非常简单,复杂的是它对应的驱动程序。在驱动程序里面,肯定会根据reg获得寄存器的地址,然后设置各种内容。


    大部分的芯片为了省电,它的外部模块时钟平时都是关闭的,只有在使用某个模块时,才设置相应的寄存器开启对应的时钟。


    这些使用者各有不同,要怎么描述这些使用者呢?

    我们可以为它们配上一个ID。在设备树中的#clock-cells = <1>;表示 用多少个u32位来描述消费者。在本例中使用一个u32来描述。


    这些ID值由谁提供的?

    是由驱动程序提供的,该节点会对应一个驱动程序,驱动程序给硬件(消费者)都分配了一个ID,所以说复杂的操作都留给驱动程序来做。

    LCD时钟设备树描述

    消费者想使用时钟时,首先要找到时钟的直接提供者,向它发出申请。以LCD为例:

        fb0: fb@4d000000{
            compatible = "jz2440,lcd";
            reg = <0x4D000000 0x60>;
            interrupts = <0 0 16 3>;
            clocks = <&clocks HCLK_LCD>;
            clock-names = "lcd";
            ……
        }

    clock属性里,首先要确定向谁发出时钟申请,这里是向clocks发出申请,然后确定想要时钟提供者提供哪一路时钟,这里是HCLK_LCD,在驱动程序里定义了该宏,每种宏对应了一个时钟ID。

    定义如下:

    ……
    /* hclk-gates */
    #define HCLK_LCD        32
    #define HCLK_USBH        33
    #define HCLK_USBD        34
    #define HCLK_NAND        35
    #define HCLK_CAM        36
    ……

    因此,我们只需要在设备节点定义clocks这个属性,这个属性确定时钟提供者,然后确定时钟ID,也就是向时钟提供者申请哪一路时钟。

    对应的内核文档可以参考这两个文件:

    Documentation/devicetree/bindings/clock/clock-bindings.txt
    Documentation/devicetree/bindings/clock/samsung,s3c2410-clock.txt

    那么我这个设备驱动程序,怎么去使用这些时钟呢? 以前的驱动程序:clk_get(NULL, "name"); clk_prepare_enable(clk); 现在的驱动程序:of_clk_get(node, 0); clk_prepare_enable(clk);

    总结

    a. 设备树中定义了各种时钟, 在文档中称之为"Clock providers", 比如:

        clocks: clock-controller@4c000000 {
            compatible = "samsung,s3c2440-clock";
            reg = <0x4c000000 0x20>;
            #clock-cells = <1>;      // 想使用这个clocks时要提供1个u32来指定它, 比如选择这个clocks中发出的LCD时钟、PWM时钟
        };

    b. 设备需要时钟时, 它是"Clock consumers", 它描述了使用哪一个"Clock providers"中的哪一个时钟(id), 比如:

        fb0: fb@4d000000{
            compatible = "jz2440,lcd";
            reg = <0x4D000000 0x60>;
            interrupts = <0 0 16 3>;
            clocks = <&clocks HCLK_LCD>;  // 使用clocks即clock-controller@4c000000中的HCLK_LCD        
        };

    c. 驱动中获得/使能时钟:

        // 确定时钟个数
        int nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
                            "#clock-cells");
        // 获得时钟
        for (i = 0; i < nr_pclks; i++) {
            struct clk *clk = of_clk_get(dev->of_node, i);
        }
    
        // 使能时钟
        clk_prepare_enable(clk);
    
        // 禁止时钟
        clk_disable_unprepare(clk);

    第六课:在LCD驱动中使用设备树

  • 相关阅读:
    开源操作系统发行版特性学习
    ssar
    OpenEuler特性学习 —— 统一共享内存 (share_pool)
    OpenEuler特性学习 —— Limit Pagecache
    oomd、lmkd与PSI
    使用git rebase onto一例
    Alibaba Cloud Linux 资源隔离及混部技术
    sysAK
    walk_tg_tree_from的图解
    编译ubuntu内核
  • 原文地址:https://www.cnblogs.com/dream397/p/15629567.html
Copyright © 2020-2023  润新知