Wednesday 14 October 2015

Device Tree in Linux

A device tree is the tree data structure that describes the physical devices in the system hardware. In Linux, the device tree source (DTS) files are located in arch/<xxx>/boot/dts

  •   dts for board level files
  •   dtsi for Soc level definitions

A device tree compiler (DTC) compiles the source into binary files. The DTC source code is in scripts/dtc.c.

The device tree blob (DTB) is produced by the DTC compiler. It is loaded by the bootloader and parsed by kernel at boot time.

Let's look at an real example where a platform driver is using two clocks.

In xxx_probe(struct platform_device *pdev)
    my_port->aaclk = clk_get(&pdev->dev, "aa");
    my_port->bbclk = clk_get(&pdev->dev, "bb");

The corresponding device tree has to reflect the two clocks.

uart0: serial@3000 {
compatible = "xxx,uart";
reg = <0x3000 0x100>;
interrupt-parent = <&gic>;
interrupts = <11 12>;
clocks = <&oscclk 0>,<&oscclk 1>;
clock-names = "aa","bb";
};

The two clock-names needs to have two matching clocks source.

Let's look at at an advanced example of device tree usage:


This example shows an oscillator clock being fed to pll1, pll2 and pll3. The hw3_clk can be derived from either pll2, pll3 or osc clock. 

To model the hardware using device tree. We declare the following entries.

osc: oscillator {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <20000000>;
clock-output-names = "osc20M";
};

pll2: pll2 {
 #clock-cells = <0>;
compatible = "abc123,pll2-clock";
clock-frequency = <23000000>;
clocks = <&osc>;
reg = <0x05 0x10>;
};

pll3: pll3 {
 #clock-cells = <0>;
compatible = "abc123,pll3-clock";
clock-frequency = <23000000>;
clocks = <&osc>;
reg = <0x15 0x10>;
};

hw3_clk: hw3_clk { 
    #clock-cells = <0>; 
    compatible = "abc123,hw3-clk"; 
    clocks = <&pll2>,<&pll3>; 
    clock-output-names = "hw3_clk"; 
}; 

In the source code, to register hw_clk3 as a mux, and show the parent relationship of both pll2 and pll3, we declare the below.

of_property_read_string(node, "clock-output-names", &clk_name); 
parent_name[0] = of_clk_get_parent_name(node, 0); 
parent_name[1] = of_clk_get_parent_name(node, 1); 

clk = clk_register_mux(NULL, clk_name, parent_name, 
      ARRAY_SIZE(parent_name), 0, 
      regs_base , offset_bit, one_bit, 0, NULL);