時鍾框圖
先來看看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);
