Overview

A Device Tree is a data structure that describes the underlying hardware to an operating system – primarily Linux.By passing this data structure the OS kernel, a single OS binary may be able to support many variations of hardware. This flexibility is particularly important when the hardware includes an FPGA.

The Device Tree Generator tool is part of Altera SoC EDS and is used to create device trees for SoC systems that contain FPGA designs created using Qsys. The generated Device Tree describes the HPS peripherals, selected FPGA Soft IP and also peripherals that are board dependent.

The Altera SoC EDS can be downloaded from https://www.altera.com/download/software/soc-eds.

Open Source

The Device Tree Generator (sopc2dts) is an Open Source Java program initially used by the NIOS community to create Device Trees for Linux running on NIOS II processors.

Altera added support for generating Device Trees for Linux running on SoC FPGA devices, and upstreamed the changes to the official repository at git://sopc.et.ntust.edu.tw/git/tools.git which is mirrored at http://git.rocketboards.org/sopc-tools.git.

The tool supports more usage scenarios than what is described in this user guide, and Altera made every effort to keep existing functionality intact. However, Altera does not provide support for the usage scenarios not described in this user guide. Please refer to http://www.alterawiki.com/wiki/Sopc2dts for more information on the sopc2dts.

Introduction to Device Trees

By using a Device Tree to configure a kernel at runtime, rather than compiled code, a single kernel binary can be used on a wide variety of boards and FPGA designs.

A typical Device Tree contains some or all of the following:
  • Number of CPUs
  • Size and location of various RAMs
  • Buses and bridges
  • Peripheral device connections
  • Interrupt controllers and IRQ line connections
  • Specific device driver configuration, such as:
    • Ethernet MAC address
    • Peripheral’s input clock

To use Device Trees, one starts with a textual representation called a Device Tree Source (DTS).The DTS is then compiled into a binary representation called the Device Tree Blob (DTB).The DTB is then handed to the kernel at boot time.

The Device Tree Compiler (dtc) is built as part of the Linux kernel build, and it is also available as part of the SoC EDS.

Using Device Trees reduces the need to recompile the kernel, but it does not completely eliminate the need to recompile a kernel when a FPGA design changes.Specifically, if a new component is added that requires a new driver and/or a new kernel configuration, the Linux kernel will need to be recompiled.

Note that full kernel recompiles can be reduced if drivers are built as dynamically loadable modules. However, Device Trees are cannot be used for dynamically loaded modules.

Device Tree Syntax

This section presents a Device Tree example and also some considerations about the syntax. Please refer to the materials in the Reference Material section for complete details.

As its name implies, a Device Tree is organized in a hierarchical way. It is composed of a root node, which has child nodes. Each child node can in turn have other child nodes. Each node can have a number of properties.

The following listing presents a part of the Device Tree created by the Device Tree Generator based on the Golden Hardware Reference Design. Only some HPS related elements are presented, to help understand the concepts.
/ {
    model = "ALTR,soc_system";
    compatible = "altr,socfpga";    
    #address-cells = < 1 >;
    #size-cells = < 1 >;

    cpus {
        #address-cells = < 1 >;
        #size-cells = < 0 >;

        hps_0_arm_a9_0: cpu@0x0 {
            device_type = "cpu";
            compatible = "arm,cortex-a9-1.0", "arm,cortex-a9";
            reg = < 0x00000000 >;
            next-level-cache = < &hps_0_L2 >;    
        }; 

        hps_0_arm_a9_1: cpu@0x1 {
            device_type = "cpu";
            compatible = "arm,cortex-a9-1.0", "arm,cortex-a9";
            reg = < 0x00000001 >;
            next-level-cache = < &hps_0_L2 >;    
        }; 
    }; 

    memory@0 {
        device_type = "memory";
        reg = < 0xC0000000 0x00010000 
            0xFFFF0000 0x00010000 >;
    }; 

    sopc0: sopc@0 {
        device_type = "soc";
        ranges;
        #address-cells = < 1 >;
        #size-cells = < 1 >;
        compatible = "ALTR,avalon", "simple-bus";
        bus-frequency = < 0 >;

        hps_0_bridges: bridge@0xc0000000 {
            compatible = "altr,bridge-1.0", "simple-bus";
            reg = < 0xC0000000 0x20000000 
                0xFF200000 0x00200000 >;
            reg-names = "axi_h2f", "axi_h2f_lw";
            #address-cells = < 2 >;
            #size-cells = < 1 >;
            ranges = < 0x00000000 0x00000000 0xC0000000 0x00010000 
                0x00000001 0x00010000 0xFF210000 0x00000008 
                0x00000001 0x00010040 0xFF210040 0x00000020 
                0x00000001 0x00010080 0xFF210080 0x00000010 
                0x00000001 0x000100C0 0xFF2100C0 0x00000010 
                0x00000001 0x00020000 0xFF220000 0x00000008 >;

            sysid_qsys: sysid@0x100010000 {
                compatible = "ALTR,sysid-13.1", "ALTR,sysid-1.0", "altr,sysid-1.0";
                reg = < 0x00000001 0x00010000 0x00000008 >;
                id = < 2899645195 >;    
                timestamp = < 1376585453 >;    
            }; 

            led_pio: gpio@0x100010040 {
                compatible = "ALTR,pio-13.1", "ALTR,pio-1.0", "altr,pio-1.0";
                reg = < 0x00000001 0x00010040 0x00000020 >;
                width = < 4 >;    
                resetvalue = < 0 >;    
                #gpio-cells = < 2 >;
                gpio-controller;
            }; 

            …
        }; 

        hps_0_arm_gic_0: intc@0xfffed000 {
            compatible = "arm,cortex-a9-gic-1.0", "arm,cortex-a9-gic";
            reg = < 0xFFFED000 0x00001000 
                0xFFFEC100 0x00000100 >;
            reg-names = "axi_slave0", "axi_slave1";
            interrupt-controller;
            #interrupt-cells = < 3 >;
        }; 

        hps_0_clkmgr: clkmgr@0xffd04000 {
            compatible = "altr,clk-mgr-1.0", "altr,clk-mgr";
            reg = < 0xFFD04000 0x00001000 >;

            clocks: clocks {
                #size-cells = < 0 >;    
                #address-cells = < 1 >;    

                sdram_pll: sdram_pll {
                    #address-cells = < 1 >;    
                    #size-cells = < 0 >;    
                    #clock-cells = < 0 >;    
                    compatible = "altr,socfpga-pll-clock";    
                    reg = < 0x000000C0 >;    
                    clocks = < &osc1 >;    

                    ddr_dqs_clk: ddr_dqs_clk {
                        #clock-cells = < 0 >;    
                        compatible = "altr,socfpga-perip-clk";    
                        clocks = < &sdram_pll >;    
                        reg = < 0x000000C8 >;    
                    }; 
                    …
                }; 

                …

                osc1: osc1 {
                    #clock-cells = < 0 >;    
                    compatible = "fixed-clock";    
                    clock-frequency = < 25000000 >;    
                }; 

                … 
            }; 
        }; 

        …

        hps_0_uart0: serial@0xffc02000 {
            compatible = "snps,dw-apb-uart-1.0", "snps,dw-apb-uart";
            reg = < 0xFFC02000 0x00001000 >;
            interrupt-parent = < &hps_0_arm_gic_0 >;
            interrupts = < 0 162 4 >;
            reg-io-width = < 4 >;    
            reg-shift = < 2 >;    
            clock-frequency = < 100000000 >;    
        }; 

        …

        hps_0_i2c0: i2c@0xffc04000 {
            compatible = "snps,designware-i2c-1.0", "snps,designware-i2c";
            reg = < 0xFFC04000 0x00001000 >;
            interrupt-parent = < &hps_0_arm_gic_0 >;
            interrupts = < 0 158 4 >;
            emptyfifo_hold_master = < 1 >;    
            #address-cells = < 1 >;
            #size-cells = < 0 >;
            speed-mode = < 0 >;    
            clocks = < &per_base_clk >;    

            lcd: newhaven,nhd-0216k3z-nsw-bbw@0x28 {
                compatible = "newhaven,nhd-0216k3z-nsw-bbw";
                reg = < 0x00000028 >;
                height = < 2 >;    
                width = < 16 >;    
                brightness = < 8 >;    
            }; 

            eeprom: atmel,24c32@0x51 {
                compatible = "atmel,24c32";
                reg = < 0x00000051 >;
                pagesize = < 32 >;    
            }; 
        }; 

        …

    }; 
    …
}; 

The nodes represented in the above listing can be summarized as:
  • root node "/"
    • cpus
      • hps_0_arm_a9_0
      • hps_0_arm_a9_1
    • memory
    • sopc0
      • hps_0_bridges
        • sysid_qsys
        • led_pio
      • hps_0_arm_gic_0
      • hps_0_clkmgr
        • clocks
          • sdram_pll
            • ddr_dqs_clk
          • osc1
      • hps_0_uart0
      • hps_0_i2c0
        • lcd
        • eeprom

The above hierarchy closely matches the actual hardware, with the CPUs, memory, peripherals connected on the bus, and LCD and EEPROM chips connected on the I2C bus. Only the ‘sopc’ node is a virtual one, it does not have a physical counterpart.

Compatible Property

Each node has a %compatible property. This is used to indicate to the kernel which driver to load for the corresponding peripheral. The compatible property generally contains the manufacturer name and the peripheral name, separated by a comma. Multiple values can be provided, in the order of preference. The kernel will try them in order until it finds a matching driver.

Addressing Scheme

The #address-cells and #size-cells properties of a node determine how its child nodes are addressed. The #address-cells indicate how many integers are used to describe the address, while the #size-cells indicate how many integers are used to describe the size of an address range allocated to the node.

Each node also has a reg property. This property is usually used to indicate the physical address of the peripheral. It consists of a set of “address length” tuples. The #address-cells and #size-cells properties of the parent will determine the exact format.The first address in the list of tuples is also usually displayed after the node name, separated with the @ symbol.

In the above example the cpus node has #address-cells=1 and #size-cells=0. This means that the address of its children has only one integer value, and that it has no concept of an address range.

Another example is the memory node, which, as a child of the root node, has #address-cells=1 and #size-cells=1. Note how there are two different memory regions each with its own address and size defined by the reg property.

Interrupts

Interrupts are generated by peripherals and routed to interrupt controllers.

The interrupt controller nodes need to have an empty property named interrupt-controller to specify that they are an interrupt controller. They also need to have a #interrupt-cells property that describes how an interrupt that is connected to it is identified. This is similar to the #address-cells used for addressing.

A peripheral that can generate interrupts needs to have an interrupt-parent property to specify where the generated interrupts go. If the node does not define an interrupt-parent property, it can inherit the interrupt-parent property of its parent node.

The peripheral that generates interrupts uses the interrupts property to specify which interrupts it generates. For each interrupt it will use the number of cells specified in the #interrupt-cells property of the interrupt-parent node.

Device Tree Bindings

The meaning of the information from the Device Tree for the actual Linux peripheral drivers is described in the Device Tree Bindings documents. They are located in the kernel’s Documentation/devicetrees/bindings folder.

For example the document Documentation/devicetree/bindings/arm/gic.txtpresents, among others, the meaning of the three interrupt cells that describe each interrupt for the Cortex GIC:
- #interrupt-cells : Specifies the number of cells needed to encode an interrupt source.
                     The type shall be a <u32>and the value is 3.

The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI interrupts.

The 2nd cell contains the interrupt number for the interrupt type. 
SPI interrupts are in the range [0-987].
PPI interrupts are in therange [0-15].

The 3rd cell is the flags, encoded as follows:
   bits[3:0] trigger type and level flags.
      1 = low-to-high edge triggered
      1 = high-to-low edge triggered
      1 = active high level-sensitive
      1 = active low level-sensitive
   bits[15:8] PPI interrupt cpu mask.

Reference Material

Device Tree Generation

The contents of a typical SOC are generally quite static. Therefore the Device Tree Source describing the SOC is quite static and is typically created by hand editing a text file. However, when a board contains an FPGA, which can contain complicated designs that can be frequently changed a tool for creating the Device Tree Source is very useful.

Device Tree Flow

The picture below depicts the entire flow of generating a Device Tree.

device-tree-flow.png

The Device Tree generation flow starts with a generated Qsys design that produces a sopcinfo file.

A user must then create an optional file that contains board specific information (See section Required Board Information for examples of the board specific information, and see section Board Information Syntax for the format of the board information file).

For Linux versions greater than 3.8 an additional board information file needs to be used to describe the device clock tree. The syntax of the file is the same as for the board specific XML file.

The Device Tree Generator takes the sopcinfo file, the board information file(s), and command line parameters as inputs and produces Device Tree Source (DTS). The standard Device Tree Compiler is used to create a Device Tree Blob (DTB). It is the DTB that is passed to the kernel.

Invoking Device Tree Generator

The Device Tree Generator application is called sopc2dts and can be invoked from command line or from Embedded Command Shell.

The sopc2dtssupports a variety of options, as can be observed by invoking it with the "--help" option. However, Altera is supporting only the following invocation scenario:
sopc2dts --input --output [--board ] [-v]

Options
  • The --input option specifies the sopcinfo file to be used
  • The --output option indicates the name of the DTS file to be created
  • The --board option indicates which board information file to be used
  • The -v option, if used, instructs the tool to run in the verbose mode.

A sample usage scenario, which recreates the GHRD DTS is shown below:
$ cd embedded/examples/hardware/cv_soc_devkit_ghrd
$ sopc2dts --input soc_system.sopcinfo --output new_soc_system.dts --board soc_system_board_info.xml --board hps_clock_info.xml

GHRD Files

The GHRD (Golden Hardware Reference Design) is released as part of the GSRD (Golden System Reference Design) at http://releases.rocketboards.org.

Note: currently, the latest version is available at https://releases.rocketboards.org/2013.11/gsrd/ghrd.

The GHRD archive files contain the hardware design and also the board XML files and the clocking XML files.

Please refer to GSRD v13.1 - Generating the Device Tree for instructions on how to generate the DTS and the DTB files for the GHRD.

Board Information

There are a number of components that can be connected to HPS portion of the SoC FPGA that require Device Tree information for their drivers. Since Qsys not aware of thecomponents outside the FPGA fabric, this information must be provided to the Device Tree Generator.

Board Information Syntax

The board information uses an XML file format and it consists of a few basic tags that allow additional DTS content to be generated by the Device Tree Generator.

This section presents the available tags and some examples, extracted from the GHRD files. See GHRD Files section for a list of the relevant GHRD files.

pov Tag

The pov tag specifies which point of view is used when generating the Device Tree Source. This needs to be set to the first Cortex A9 ARM core:
<BoardInfo pov="hps_0_arm_a9_0">

alias Tag

The aliastag is used to create aliases in the generated Device Tree Source. For example the following board information XML fragment:
<alias name="ethernet0" value="/sopc/ethernet@0xff702000"/>
<alias name="serial0" value="/sopc/serial@0xffc02000"/>
<alias name="timer0" value="/sopc/timer@0xffc08000"/>
<alias name="timer1" value="/sopc/timer@0xffc09000"/>
<alias name="timer2" value="/sopc/timer@0xffd00000"/>
<alias name="timer3" value="/sopc/timer@0xffd01000"/>

produces the following Device Tree Source output:
aliases {
    ethernet0 = "/sopc/ethernet@0xff702000";
    serial0 = "/sopc/serial@0xffc02000";
    timer0 = "/sopc/timer@0xffc08000";
    timer1 = "/sopc/timer@0xffc09000";
    timer2 = "/sopc/timer@0xffd00000";
    timer3 = "/sopc/timer@0xffd01000";
};

Chosen Tag

The Chosentag is used to pass the kernel boot parameters.For example the following board information XML fragment:
<Chosen>
        <Bootargs val="console=ttyS0,57600"></Bootargs>
</Chosen>

produces the following DTS output:
chosen {
    bootargs = "console=ttyS0,57600";
};

DTAppend Tag

The DTAppendtag is used to add properties and nodes. For example the following board info fragment:
<DTAppend name="leds" type="node" parentlabel="sopc0" newlabel="soc_leds"/>
<DTAppend name="compatible" type="string" parentlabel="soc_leds" val="gpio-leds"/>
<DTAppend name="hps0" type="node" parentlabel="soc_leds" newlabel="led_hps0"/>
<DTAppend name="label" type="string" parentlabel="led_hps0" val="hps_led0"/>
<DTAppend name="gpios" parentlabel="led_hps0" >
      <val type="phandle">hps_0_gpio1</val>
      <val type="number">15</val>
      <val type="number">1</val>
</DTAppend>

produces the following Device Tree Source output:
soc_leds: leds {
    compatible = "gpio-leds";    
    led_hps0: hps0 {
        label = "hps_led0";    
        gpios = < &hps_0_gpio1 15 1 >;    
    }; 
};

As it can be seen, the DTAppend tag can be used to add nodes, sub nodes and properties. It is a powerful tool that could be used to create any desired DTS output.

I2CBus and I2CChip Tags

The I2CBus and I2CChip tags are used to add custom external chips on the I2C bus.

The following sample board information fragment:
<I2CBus master="hps_0_i2c0">
    <I2CChip addr="0x28" label="lcd" name="newhaven,nhd-0216k3z-nsw-bbw"></I2CChip>
    <I2CChip addr="0x51" label="eeprom" name="atmel,24c32"></I2CChip>
</I2CBus>
<DTAppend name="height" type="number" parentlabel="lcd" val="2"/>
<DTAppend name="width" type="number" parentlabel="lcd" val="16"/>
<DTAppend name="brightness" type="number" parentlabel="lcd" val="8"/>
<DTAppend name="pagesize" type="number" parentlabel="eeprom" val="32"/>

creates the lcd and eeprom nodes as children of the hps_0_i2c0node:
hps_0_i2c0: i2c@0xffc04000 {
    …
    lcd: newhaven,nhd-0216k3z-nsw-bbw@0x28 {
        compatible = "newhaven,nhd-0216k3z-nsw-bbw";
        reg = < 0x00000028 >;
        height = < 2 >;    
        width = < 16 >;    
        brightness = < 8 >;    
    }; 
    eeprom: atmel,24c32@0x51 {
        compatible = "atmel,24c32";
        reg = < 0x00000051 >;
        pagesize = < 32 >;    
    };     
    …    
};

IRQMasterIgnore Tag

There are cases where interrupt senders are connected to multiple interrupt receivers (e.g. an interrupt controller and an interrupt debug component), but the device tree needs to identify the correct interrupt parent/controller. To help the Device Tree Generator identify the correct interrupt connection, one can use one or more IRQMasterIgnoretags in the board info file like the example below.
<IRQMasterIgnore className="intr_capturer"/>

Required Board Information

This section presents the required information that needs to be provided in the board information XML file in order to boot the Linux Kernel and configure each device module/driver. The information is presented in both Board Infromation XML and in DTS (Device Tree Source) format. To download a minimalistic board info XML file that contains only those properties needed to boot the kernel please click here: boardinfo_min.xml. This file can be editted further to enable/disable the peripheral devices in your system.

Point of View

The POV Tag is required and the value must match the name of the first Cortex-A9 ARM core.
<BoardInfo pov="hps_0_arm_a9_0">

Required Root Node Properties

The following are the required root node properties. Both the model and compatible properties must be set.

Board Info XML:
<DTAppend name="model" type="string" parentlabel="" val="Altera SOCFPGA Cyclone V"/>
<DTAppend name="compatible" parentlabel="" >
<val type="string">altr,socfpga-cyclone5</val>
<val type="string">altr,socfpga</val>
</DTAppend>

Resulting DTS:
model = "Altera SOCFPGA Cyclone V"; 
compatible = "altr,socfpga-cyclone5", "altr,socfpga";

Required Timer Peripheral Properties

The following are the required timer properties to boot the Linux Kernel. The Kernel needs to know the clock frequencies of each timer to properly configure.

Board Info XML:
<DTAppend name="clock-frequency" type="number" parentlabel="hps_0_timer0" val="100000000"/>
<DTAppend name="clock-frequency" type="number" parentlabel="hps_0_timer1" val="100000000"/>
<DTAppend name="clock-frequency" type="number" parentlabel="hps_0_timer2" val="25000000"/>
<DTAppend name="clock-frequency" type="number" parentlabel="hps_0_timer3" val="25000000"/>

Resulting DTS:
TBD

Required System Manager Peripheral Properties

The following are the required System Manager properties to boot the Linux Kernel. These properties are used to link the sysmgr hardware to the System Control kernel module. If these properties are not included, then further peripheral configuration may fail during Linux boot.

Board Info XML:
<DTAppend name="cpu1-start-addr" type="hex" parentlabel="hps_0_sysmgr" val="0xffd080c4"/>
<DTAppend name="compatible" type="string" parentlabel="hps_0_sysmgr" val="syscon" action="add"/>

Resulting DTS:
hps_0_sysmgr: sysmgr@0xffd08000 {
compatible = "altr,sys-mgr-1.0", "altr,sys-mgr", "syscon";
reg = < 0xFFD08000 0x00004000 >;
cpu1-start-addr = < 0xFFD080C4 >;
};

Required UART Peripheral Properties

All UARTs have at least one input clock which drives the baud rate, and they may have a second clock that drives register accesses.The hardened UART on the HPS has a single clock used for both functions.The actual input clock would typically be setup by the Preloader software, but the Linux kernel needs to know the clock rate for baud rate divisor calculations.The clock frequency to the hardened UARTs must be provided as board information by the user.

Board Info XML:
<DTAppend name="clock-frequency" type="number" parentlabel="hps_0_uart0" val="100000000"/>

Resulting DTS:
hps_0_uart0: serial@0xffc02000 {
    …
    clock-frequency = < 100000000 >;    
    …
}; 

Required QSPI Flash Peripheral Properties

The QSPI driver requires the following information about the external flash connected to the controller. Below are the fields that need to be specified as Board Information. In addition to the flash information shown, the partition information of the QSPI flash will also need to be provided.

Board Info XML:
<BoardInfo>
<DTAppend name="master-ref-clk" parentlabel="hps_0_qspi" >
<val type="number">400000000</val>
</DTAppend>
<DTAppend name="ext-decoder" parentlabel="hps_0_qspi" >
<val type="number">0</val>
</DTAppend>
<DTAppend name="#address-cells" parentlabel="hps_0_qspi" >
<val type="number">1</val>
</DTAppend>
<DTAppend name="#size-cells" parentlabel="hps_0_qspi" >
<val type="number">0</val>
</DTAppend>
<DTAppend name="n25q00@0" type="node" parentlabel="hps_0_qspi" newlabel="flash0"/>
<DTAppend name="#address-cells" parentlabel="flash0" >
<val type="number">1</val>
</DTAppend>
<DTAppend name="#size-cells" parentlabel="flash0" >
<val type="number">1</val>
</DTAppend>
<DTAppend name="compatible" type="string" parentlabel="flash0" val="n25q00"/>
<DTAppend name="reg" parentlabel="flash0" >
<val type="number">0</val>
</DTAppend>
<DTAppend name="spi-max-frequency" parentlabel="flash0" >
<val type="number">100000000</val>
</DTAppend>
<DTAppend name="page-size" parentlabel="flash0" >
<val type="number">256</val>
</DTAppend>
<DTAppend name="block-size" parentlabel="flash0" >
<val type="number">16</val>
</DTAppend>
<DTAppend name="read-delay" parentlabel="flash0" >
<val type="number">4</val>
</DTAppend>
<DTAppend name="tshsl-ns" parentlabel="flash0" >
<val type="number">50</val>
</DTAppend>
<DTAppend name="tsd2d-ns" parentlabel="flash0" >
<val type="number">50</val>
</DTAppend>
<DTAppend name="tchsh-ns" parentlabel="flash0" >
<val type="number">4</val>
</DTAppend>
<DTAppend name="tslch-ns" parentlabel="flash0" >
<val type="number">4</val>
</DTAppend>
<DTAppend name="partition@0" type="node" parentlabel="flash0" newlabel="part0"/>
<DTAppend name="label" type="string" parentlabel="part0" val="Flash 0 Raw Data"/>
<DTAppend name="reg" parentlabel="part0" >
<val type="hex">0x00000000</val>
<val type="hex">0x00800000</val>
</DTAppend>
<DTAppend name="partition@800000" type="node" parentlabel="flash0" newlabel="part1"/>
<DTAppend name="label" type="string" parentlabel="part1" val="Flash 1 jffs2 Filesystem"/>
<DTAppend name="reg" parentlabel="part1" >
<val type="hex">0x00800000</val>
<val type="hex">0x00800000</val>
</DTAppend>
</BoardInfo>

Resulting DTS:
hps_0_qspi: flash@0xff705000 {
    master-ref-clk = < 400000000 >;    
    ext-decoder = < 0 >;    
    #address-cells = < 1 >;    
    #size-cells = < 0 >;    

    flash0: n25q00@0 {
        #address-cells = < 1 >;    
        #size-cells = < 1 >;    
        compatible = "n25q00";    
        reg = < 0 >;    
        spi-max-frequency = < 100000000 >;    
        page-size = < 256 >;    
        block-size = < 16 >;    
        m25p,fast-read;    
        read-delay = < 4 >;    
        tshsl-ns = < 50 >;    
        tsd2d-ns = < 50 >;    
        tchsh-ns = < 4 >;    
        tslch-ns = < 4 >;    

        part0: partition@0 {
            label = "Flash 0 Raw Data";    
            reg = < 0x00000000 0x00800000 >;    
        }; 

        part1: partition@800000 {
            label = "Flash 1 jffs2 Filesystem";    
            reg = < 0x00800000 0x00800000 >;    
        }; 
    }; 
}; 

Required NAND Flash Peripheral Properties

The NAND controller will probe the flash for any necessary information about the connected flash device required by the driver.No information needs to be provided by the user regarding the flash device. However, the user has to provide partition information.

Board Info XML:
<BoardInfo>
<DTAppend name="nand@ff900000" type="node" parentlabel="sopc0" newlabel="nand"/>
<DTAppend name="label" type="string" parentlabel="nand" val="U-Boot"/>
<DTAppend name="reg" parentlabel="nand" >
<val type="hex">0x0</val>
<val type="hex">0x40000</val>
</DTAppend>
<DTAppend name="label" type="string" parentlabel="sopc0" val="Kernel"/>
<DTAppend name="reg" parentlabel="sopc0" >
<val type="hex">0x40000</val>
<val type="hex">0x3c0000</val>
</DTAppend>
</BoardInfo>

Resulting DTS:
nand: nand@ff900000 {
     …
     partition@0 {
        label = "U-Boot";
        reg = <0x0 0x40000>;
     };
     partition@40000 {
        label = "Kernel";
        reg = <0x40000 0x3c0000>;
     };
     …
};

Note that the Cyclone V SoC Development Kit does not have an on-board NAND flash memory device, therefor the GHRD board information file disables the NAND peripheral. Remove the status line to enable.
nand: nand@ff900000 {
     …
     status = "disabled";
     …
};

Required SDMMC Peripheral Properties

The SDMMC driver requires the following board specific information. This information is required if booting from an external MMC card.

Board Info XML:
<DTAppend name="clocks" parentlabel="hps_0_sdmmc" >
<val type="phandle">l4_mp_clk</val>
<val type="phandle">sdmmc_clk</val>
</DTAppend>
<DTAppend name="clock-names" parentlabel="hps_0_sdmmc" >
<val type="string">biu</val>
<val type="string">ciu</val>
</DTAppend>
<DTAppend name="#address-cells" type="number" parentlabel="hps_0_sdmmc" val="1"/>
<DTAppend name="#size-cells" type="number" parentlabel="hps_0_sdmmc" val="0"/>
<DTAppend name="supports-highspeed" parentlabel="hps_0_sdmmc" />
<DTAppend name="broken-cd" type="bool" parentlabel="hps_0_sdmmc" val="true"/>
<DTAppend name="compatible" type="string" parentlabel="hps_0_sdmmc" val="altr,socfpga-dw-mshc" action="replace"/>
<DTAppend name="altr,dw-mshc-ciu-div" type="number" parentlabel="hps_0_sdmmc" val="3"/>
<DTAppend name="altr,dw-mshc-sdr-timing" parentlabel="hps_0_sdmmc" >
<val type="number">0</val>
<val type="number">3</val>
</DTAppend>
<DTAppend name="slot@0" type="node" parentlabel="hps_0_sdmmc" newlabel="slot_0"/>
<DTAppend name="reg" type="number" parentlabel="slot_0" val="0"/>
<DTAppend name="bus-width" type="number" parentlabel="slot_0" val="4"/>

Resulting DTS:
hps_0_sdmmc: flash@0xff704000 {
    compatible = "altr,socfpga-dw-mshc";    
    reg = < 0xFF704000 0x00001000 >;
    interrupt-parent = < &hps_0_arm_gic_0 >;
    interrupts = < 0 139 4 >;
    fifo-depth = < 1024 >;    
    num-slots = < 1 >;    
    bank-width = < 2 >;
    device-width = < 1 >;
    clocks = < &l4_mp_clk &sdmmc_clk >;    
    clock-names = "biu", "ciu";    
    #address-cells = < 1 >;    
    #size-cells = < 0 >;    
    supports-highspeed;    
    broken-cd;    
    altr,dw-mshc-ciu-div = < 3 >;    
    altr,dw-mshc-sdr-timing = < 0 3 >;    

    slot_0: slot@0 {
        reg = < 0 >;    
        bus-width = < 4 >;    
    }; 
};

Required Ethernet Peripheral Properties

The board specific information required for Ethernet drivers is the same for both the hardened EMACs and soft EMAC implementations.The Device Tree specifies the MAC address to be used by the EMAC.In practice the MAC address is also used by the EMAC driver in U-boot; so it is typically stored in flash that is accessible to U-boot.U-boot then updates the DTB with MAC before transferring control to the Linux kernel.

The other board specific information for Ethernet describes the type of PHY connected to the EMAC controller (e.g. MII, RGMII, etc.).

Board Info XML:
<BoardInfo>
<DTAppend name="ethernet@0xff702000" type="node" parentlabel="sopc0" newlabel="hps_0_gmac1"/>
<DTAppend name="phy-mode" type="string" parentlabel="hps_0_gmac1" val="rgmii"/>
<DTAppend name="clock-names" type="string" parentlabel="hps_0_gmac1" val="stmmaceth"/>
<DTAppend name="clocks" parentlabel="hps_0_gmac1" >
<val type="number">&emac1_clk</val>
</DTAppend>
<DTAppend name="phy-addr" parentlabel="hps_0_gmac1" >
<val type="hex">0xFFFFFFFF</val>
</DTAppend>
<DTAppend name="micrel-ksz9021rlrn-clk-skew" parentlabel="hps_0_gmac1" >
<val type="hex">0x0000A0E0</val>
</DTAppend>
<DTAppend name="micrel-ksz9021rlrn-rx-skew" parentlabel="hps_0_gmac1" >
<val type="hex">0x00000000</val>
</DTAppend>
</BoardInfo>

Resulting DTS:
hps_0_gmac1: ethernet@0xff702000 {
    …
    phy-mode = "rgmii";    
    clock-names = "stmmaceth";    
    clocks = < &emac1_clk >;    
    phy-addr = < 0xFFFFFFFF >;    
    micrel-ksz9021rlrn-clk-skew = < 0x0000A0E0 >;    
    micrel-ksz9021rlrn-rx-skew = < 0x00000000 >;    
    …
}; 

Required <nop>I2C Peripheral Properties

A user may provide the desired I2C bus clock frequency and also the list of devices that are externally connected to the I2C bus.

Board Info XML:
<BoardInfo>
<DTAppend name="i2c@0xffc04000" type="node" parentlabel="sopc0" newlabel="hps_0_i2c0"/>
<DTAppend name="speed-mode" parentlabel="hps_0_i2c0" >
<val type="number">0</val>
</DTAppend>
<DTAppend name="newhaven,nhd-0216k3z-nsw-bbw@0x28" type="node" parentlabel="hps_0_i2c0" newlabel="lcd"/>
<DTAppend name="compatible" type="string" parentlabel="lcd" val="newhaven,nhd-0216k3z-nsw-bbw"/>
<DTAppend name="reg" parentlabel="lcd" >
<val type="hex">0x00000028</val>
</DTAppend>
<DTAppend name="height" parentlabel="lcd" >
<val type="number">2</val>
</DTAppend>
<DTAppend name="width" parentlabel="lcd" >
<val type="number">16</val>
</DTAppend>
<DTAppend name="brightness" parentlabel="lcd" >
<val type="number">8</val>
</DTAppend>
<DTAppend name="atmel,24c32@0x51" type="node" parentlabel="hps_0_i2c0" newlabel="eeprom"/>
<DTAppend name="compatible" type="string" parentlabel="eeprom" val="atmel,24c32"/>
<DTAppend name="reg" parentlabel="eeprom" >
<val type="hex">0x00000051</val>
</DTAppend>
<DTAppend name="pagesize" parentlabel="eeprom" >
<val type="number">32</val>
</DTAppend>
</BoardInfo>

Resulting DTS:
hps_0_i2c0: i2c@0xffc04000 {
    …
    speed-mode = < 0 >;                
    …
    lcd: newhaven,nhd-0216k3z-nsw-bbw@0x28 {
        compatible = "newhaven,nhd-0216k3z-nsw-bbw";
        reg = < 0x00000028 >;
        height = < 2 >;    
        width = < 16 >;    
        brightness = < 8 >;    
    }; 

    eeprom: atmel,24c32@0x51 {
        compatible = "atmel,24c32";
        reg = < 0x00000051 >;
        pagesize = < 32 >;    
    }; 
};

Required SPI Slave Properties

For each SPI master used, the user will have to provide information for each connected SPI slave. Each slave node must have the following fields:
  • reg - chip select address of device.
  • compatible - name of SPI device following generic names recommended practice
  • spi-max-frequency - Maximum SPI clocking speed of device in Hz

This is the corresponding DTS fragment for CV Development board - note that the SPI is actually disabled because there are no SPI devices connected to it on the board.

Board Info XML:
<BoardInfo>
<DTAppend name="spi@0xffe02000" type="node" parentlabel="sopc0" newlabel="hps_0_spi0"/>
<DTAppend name="status" type="string" parentlabel="hps_0_spi0" val="disabled"/>
<DTAppend name="spidev@0" type="node" parentlabel="hps_0_spi0" newlabel="spidev0"/>
<DTAppend name="compatible" type="string" parentlabel="spidev0" val="spidev"/>
<DTAppend name="reg" parentlabel="spidev0" >
<val type="number">0</val>
</DTAppend>
<DTAppend name="spi-max-frequency" parentlabel="spidev0" >
<val type="number">100000000</val>
</DTAppend>
<DTAppend name="enable-dma" parentlabel="spidev0" >
<val type="number">1</val>
</DTAppend>
</BoardInfo>

Resulting DTS:
hps_0_spi0: spi@0xffe02000 {
    …
    status = "disabled";    
    …
    spidev0: spidev@0 {
        compatible = "spidev";    
        reg = < 0 >;    
        spi-max-frequency = < 100000000 >;    
        enable-dma = < 1 >;    
    }; 
}; 

Disabling Peripherals

Peripherals that exist in hardware can also be disabled in the device tree. If the status property is marked disabled for the device, then this device will not be probed at boot time. The following example shows how to disable the UART1 and USB0 peripherals.
<DTAppend name="status" type="string" parentlabel="hps_0_uart1" val="disabled"/>
<DTAppend name="status" type="string" parentlabel="hps_0_usb0" val="disabled"/>

SDRAM Memory Size

The size of the SDRAM is passed to the Linux kernel either as a boot argument or as a memory node in a device tree.The current U-boot implementation ensures there is always a memory node in the device tree, which overrides any boot argument setting the SDRAM size.

Clock Information

Starting with Linux kernel version 3.8, the Device Tree file needs to also contain the clocking information. This information is provided to the Device Tree Generator using the same syntax as for the Board information file.

The example below describes the SDRAM related clocks for the Cyclone V SoC:
<DTAppend name="sdram_pll" type="node" parentlabel="clocks" newlabel="sdram_pll" />
<DTAppend name="#address-cells" type="number" parentlabel="sdram_pll" val="1" />
<DTAppend name="#size-cells" type="number" parentlabel="sdram_pll" val="0" />
<DTAppend name="#clock-cells" type="number" parentlabel="sdram_pll" val="0" />
<DTAppend name="compatible" type="string" parentlabel="sdram_pll" val="altr,socfpga-pll-clock" />

<DTAppend name="ddr_dqs_clk" type="node" parentlabel="sdram_pll" newlabel="ddr_dqs_clk" />
<DTAppend name="#clock-cells" type="number" parentlabel="ddr_dqs_clk" val="0" />
<DTAppend name="compatible" type="string" parentlabel="ddr_dqs_clk" val="altr,socfpga-perip-clk" />
<DTAppend name="clocks" type="phandle" parentlabel="ddr_dqs_clk" val="sdram_pll" />
<DTAppend name="reg" type="hex" parentlabel="ddr_dqs_clk" val="0xc8" />

<DTAppend name="ddr_2x_dqs_clk" type="node" parentlabel="sdram_pll" newlabel="ddr_2x_dqs_clk" />
<DTAppend name="#clock-cells" type="number" parentlabel="ddr_2x_dqs_clk" val="0" />
<DTAppend name="compatible" type="string" parentlabel="ddr_2x_dqs_clk" val="altr,socfpga-perip-clk" />
<DTAppend name="clocks" type="phandle" parentlabel="ddr_2x_dqs_clk" val="sdram_pll" />
<DTAppend name="reg" type="hex" parentlabel="ddr_2x_dqs_clk" val="0xcc" />
<DTAppend name="reg" type="hex" parentlabel="sdram_pll" val="0xc0" />

And this is the corresponding generated DTS:
sdram_pll: sdram_pll {
    #address-cells = < 1 >;    
    #size-cells = < 0 >;    
    #clock-cells = < 0 >;    
    compatible = "altr,socfpga-pll-clock";    
    reg = < 0x000000C0 >;    
    clocks = < &osc1 >;    

    ddr_dqs_clk: ddr_dqs_clk {
        #clock-cells = < 0 >;    
        compatible = "altr,socfpga-perip-clk";    
        clocks = < &sdram_pll >;    
        reg = < 0x000000C8 >;    
    }; 

    ddr_2x_dqs_clk: ddr_2x_dqs_clk {
        #clock-cells = < 0 >;    
        compatible = "altr,socfpga-perip-clk";    
        clocks = < &sdram_pll >;    
        reg = < 0x000000CC >;    
    };
    …
}; 

Soft IP Support

The Device Tree Generator relies on the information contained in the sopcinfo file to be able to generate the proper Device Tree entries.

In order for the information required by the Device Tree Generator to be present in the sopcinfo file for a certain Soft IP peripheral, that peripheral has to have the appropriate module assignments in the component’s _hw.tcl file.

Supported Soft IP Drivers

The following Soft IP peripherals provided by Quartus already have the required support enabled:
  • Uart
  • Jtag_uart
  • Sysid
  • Altera-pio

Note that adding support for a certain Soft IP peripheral involves creating the necessary kernel binding definition (if not already available), writing the Linux driver for it, and also adding the appropriate assignments in the component’s _hw.tcl file.

As more Linux drivers for the Altera Soft IP are developed, the required support is being added to more Soft IP peripherals.

Adding Device Tree Generation Support to an IP Block

When creating a new IP block, the IP developer needs to work closely with a member of the embedded software team to ensure a Linux driver is available for the IP block when it is released.In addition, by working with a driver writer early in the development processes, the IP block can be designed is such a way to help optimize the software driver.Furthermore, the driver itself should be written to take advantage of flexibility provided by the use of Device Trees.

To facilitate a smooth flow of Device Tree Generation for an IP block, the IP developer/driver writer team must create the appropriate module assignments in the component’s _hw.tcl file.The assignments are created using the set_module_assigment command with the assignment name having the prefix, embeddedsw.dts.

Required Assignments

The TCL snippet, shown below, is an example of the minimum set of assignments for a trivial device tree support by a driver:
# +-----------------------------------
# | Device tree generation
# |
set_module_assignment embeddedsw.dts.vendor "altr"
set_module_assignment embeddedsw.dts.compatible " snps,dw-apb-uart"
set_module_assignment embeddedsw.dts.group "serial"
# |
# +-----------------------------------

Module assignments are required for embeddedsw.dts.vendor, embeddedsw.dts.group, and embeddedsw.dts.compatible. The value for the vendor field should be the uppercase stock ticker symbol for the vendor or a lowercase vendor name.The value for group field is the generic name for the type of device.The assignment to embeddedsw.dts.compatible associates a driver to the Device Tree Node.

In the example above, the value for compatible field corresponds to the following lines in the Linux driver, drivers/tty/serial/8250/8250-dw.c:
static const struct of_device_id dw8250_match[] = {
   { .compatible = "snps,dw-apb-uart" },
   { /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw8250_match);

static struct platform_driver dw8250_platform_driver = {
   .driver = {
      .name             = "dw-apb-uart",
      .owner            = THIS_MODULE,
      .of_match_table   = dw8250_match,
   },
   .probe          = dw8250_probe,
   .remove         = __devexit_p(dw8250_remove),
};

Additional Parameters

Additional key/value pairs can be added to the Device Tree Node by assignments to embeddedsw.dts.params.<key-name>as shown in the following example:
set_module_assignment embeddedsw.dts.params.reg-io-width 4
set_module_assignment embeddedsw.dts.params.reg-shift 2
set_module_assignment embeddedsw.dts.params.tx-fifo 1024
set_module_assignment embeddedsw.dts.params.rx-fifo 1024

The assignments above result in the field assignments in the DTS shown below:
reg-io-width = < 4 >;
reg-shift = < 2 >;
tx-fifo = < 1024 >;
rx-fifo = < 1024 >;

The DTS field assignments above are cells of numerical value.The assignment below is a string value:
set_module_assignment embeddedsw.dts.params.stringkey value

The example above has a value that is a string which will result in the DTS field being show as follows:
stringkey = "value";

Protecting Field Names and Value Content

TCL is a little vague about strings versus numbers, and special characters can have unintended consequences.In some cases one might need to protect the name a field or the content of a value by surrounding them with a pair of braces, {}, as shown below.
set_module_assignment embeddedsw.dts.compatible {arm,cortex-a9-gic}
set_module_assignment {embeddedsw.dts.params.#interrupt-cells} 3

In the example above, value of the compatible field is protected because of the comma in the string, and the key specifying the number of values in an interrupt cell is protected because of the character, #.

Complex Value Content

Device Trees are extremely flexible with regards to the content of a node’s name/value pairs.A value can be string, a cell of numbers, or binary data.Furthermore, the value can be a list of any combination of the basic types.To address this problem, the notion of type will be added to the embeddeddw.dts namespace. Assignment statements whose field name begin with embeddedsw.dts.type.typename will be considered typed.The token, typename, will drive the interpretation of the value data.At this time, there is only one typenamedefined, raw.As the name implies, the content of the data will not be interpreted and consumed without modification.
set_module_assignment embeddedsw.dts.type.raw.weird-list {“foo,bar”, < 0 2 4 >, [0x02 0x03 0x04]}

The example above will produce the Device Field assignment below where the value is a list with a string, cell of numbers, and binary data:
weird-list = “foo,bar”, < 0 2 4 >, [0x02 0x03 0x04];

Reference Clocks

In some cases, a device driver needs to know the frequency an input clock.For example a UART driver needs to know the input clock frequency in order to properly calculate baud rate divisors.In these cases, the Device Tree node will have a field as follows:
clock-frequency = <freq>;

To use the value of the clock frequency supplied to the component, assign the value, →clk/clock_name,to the field, embeddedsw.dts.clock-frequency.The value of the clock supplied to the component’s signal named, clock_name, will be used.The example assignment below will set the value of the clock-frequency field to the value of the clock connected to the signal named pclk.
set_module_assignment%ENDCOLOR embeddedsw.dts.params.clock-frequency →clk/pclk

© 1999-2024 RocketBoards.org by the contributing authors. All material on this collaboration platform is the property of the contributing authors.

Privacy Policy - Terms Of Use

This website is using cookies. More info. That's Fine