Device Tree Tutorial (ARM)


Overview


 

The linux kernel requires the entire description of the hardware, like which board it is booting(machine type), which all devices it is using there addresses(device/bus addresses), there interrupts numbers(irq), mfp pins configuration(pin muxing/gpios)  also some board level information like memory size, kernel command line etc etc …

Before device tree, all these information use to be set in a huge cluster of board files. And, Information like command line, memory size etc use to be passed by bootloaders as part of ATAGS through register R2(ARM). Machine type use to be set separately in register R1(ARM).
At this time each kernel compilation use to be for only one specific chip an a specific board.

So there was a long pending wish to compile the kernel for all ARM processors, and let the kernel somehow detect its hardware and apply the right drivers as needed just like your PC.
But how? On a PC, the initial registers are hardcoded, and the rest of the information is supplied by the BIOS. But ARM processors don’t have a BIOS.
The solution chosen was device tree, also referred to as Open Firmware (abbreviated OF) or Flattened Device Tree (FDT). This is essentially a data structure in byte code format which contains information that is helpful to the kernel when booting up.

The bootloader now loads two binaries: the kernel image and the DTB.
DTB is the device tree blob. The bootloader passes the DTB address through R2 instead of ATAGS and R1 register is not required now.

For a one line bookish definition “A device tree is a tree data structure with nodes that describe the physical devices in a system”

Currently device tree is supported by ARM, x86, Microblaze, PowerPC, and Sparc architectures.

 


I. Device Tree Compilation


Device tree compiler and its source code  located at scripts/dtc/.
On ARM all device tree source are located at /arch/arm/boot/dts/.
The Device Tree Blob(.dtb) is produced by the compiler, and it is the binary that gets loaded by the bootloader and parsed by the kernel at boot time.

$ scripts/dtc/dtc -I dts -O dtb -o /path/my_tree.dtb /arch/arm/boot/dts/my_tree.dts

This will result my_tree.dtb

For creating the dts from dtb

$ scripts/dtc/dtc -I dtb -O dts -o /path/my_tree.dts /path/my_tree.dtb

This will result my_tree.dts

 


 II. Device Tree Basics


 

Each module in device tree is defined by a node and all its properties are defined under that node. Depending on the driver it can have child nodes or parent node.
For example a device connected by i2c bus, will have i2c as its parent node, and that device will be one of the child node of i2c node, i2c may have apd bus as its parent and so on. All leads up to root node, which is parent of all. (Don’t worry an example after this section will make it more clear.)
Under the root of the Device Tree, one typically finds the following most common top-level nodes:

  • cpus: its each sub-nodes describing each CPU in the system.
  • memory : defines location and size of the RAM.
  • chosen : defines parameters chosen or defined by the system firmware at boot time. In practice, one of its usage is to pass the kernel command line.
  • aliases: shortcuts to certain nodes.
  • One or more nodes defining the buses in the SoC
  • One or mode nodes defining on-board devices

 


III. Device Tree Structure example


Here will take the example of a dummy dts code for explanation

 #include "pxa910.dtsi"
/ {
    compatible = "mrvl,pxa910-dkb", "mrvl,pxa910";
    chosen {
	bootargs = "<boot args here>";
    };
    memory {
        reg = <0x00000000 0x10000000>;
    };
    soc {
	apb@d4000000 {         

	    uart1: uart@d4017000 {
	    status = "okay";
	    };
	    twsi1: i2c@d4011000 {
                #address-cells = <1>
                #size-cells = <0>
		status = "okay";
		pmic: 88pm860x@34 {
                    compatible = "marvell,88pm860x";
		    reg = <0x34>;
		    interrupts = <4>;
		    interrupt-parent = <&intc>;
		    interrupt-controller;
		    #interrupt-cells = <1>;

Figure 1

Each module is defined in one curly bracket area under one node, any sub modules can be defined further inside.

Explaning the above tree starting from the first line :

#include : including any headed file, just like any C file
.dtsi : extended dts file, single dts can have any number of dtsi, but couldn’t include other dts file
/: root node, device tree structure starts here


IV. Properties


 

There are data define in dts as form of property which are read by the kernel code, lets read about some of the major properties

Compatible

The top-level compatible property typically defines a compatible string for the board. Priority always given with the most-specific first, to least-specific last. It used to match with the dt_compat field of the DT_MACHINE structure.
Inside a driver or bus node , it is the most crucial one, as it is the link between the hardware and its driver.Each node belongs to one compatible string and based on compatible string only kernel matches the device driver with its data in device tree node.
The connection between a kernel driver and the “compatible” entries it should be attached to, is made by a code segment as follows in the driver’s source code:

static struct of_device_id dummy_of_match[] = {
  { .compatible = "marvell,88pm860x", },
    {}
  };
MODULE_DEVICE_TABLE(of, dummy_of_match);

 The above code in driver matches it to the pmic node shown in device tree structure shown in figure 1.

reg

defines the address for that node/device

#address-cells

property indicate how many cells (i.e 32 bits values) are needed to form the base address part in the reg property

#size-cells

the size part of the reg property

interrupt-controller

is a boolean property that indicates that the current node is an interrupt controller

#interrupt-cells

indicates the number of cells in the interrupts property for the interrupts managed by the selected interrupt controller

interrupt-parent

is a phandle that points to the interrupt controller for the current node. There is generally a top-level interrupt-parent definition for the main interrupt controller.

The label and node name

First, the label (”pmic”) and entry’s name (”88pm860x@34″). The label could have been omitted altogether, and the entry’s node name should stick to this format (some-name@address). This tells the kernel that this driver name 88pm860x and connected to its parent bus(i2c in this case) with the adress 34 (i2c slave address here). PMIC is the label which could be use as a phandle to refer this node inside dts.

 


 V. Getting the resources from DTS


 

Below are the few major APIs in current kernel (4.3) for reading the various properties from DTS.

of_address_to_resource: Reads the memory address of device defined by res property

irq_of_parse_and_map: Attach the interrupt handler, provided by the properties interrupt and interrupt-parent

of_find_property(np, propname, NULL): To find if property named in argument2 is present or not.

of_property_read_bool: To read a bool property named in argument 2, as it is a bool property it just like searching if that property present or not. Returns true or false

of_get_property: For reading any property named in argument 2

of_property_read_u32To read a 32 bit property, populate into 3rd argument. Doesn’t set anything to 3rd argument in case of error.

of_property_read_string: To read string property

of_match_device: Sanity check for device that device is matching with the node, highly optional, I don’t see much use of it.

 

let me know if you have any doubts related to device tree in comment section below or an personal email to me.

 

Saurabh Singh Sengar

email to: saurabh.truth@gmail.com

 

Advertisements

Kernel Patch Submission tutorial

Getting your patch submitted in Linux kernel could be one of the most satisfying job for a newbie Linux kernel developer. Until my first patch got in to mainline I was not knowing that this could be a very easy task as it seems to be.

There are many ways to submit a patch to Linux community, but will not discuss all of them as it could be very confusing. I have gone through all and came up with one most sure shot procedure.

Ok, so here is to the point step by step guide to submit your first linux kernel patch, and have your name in Linux kernel 🙂

I am considering you are doing all this in Ubuntu machine, for other flavors please tweak the commands accordingly.


 I. Tools Set up


    1) Install git : version control system for your Linux kernel

sudo apt-get install git

    2) Install git mail : you will be requiring this to send patches to Linux community

sudo apt-get install git-email

    3) Config git : create a file at your home directory vim ~/.gitconfig, and have the below details in it

[user]
name = Saurabh Sengar
email = saurabh.truth@gmail.com
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = saurabh.truth@gmail.com
smtpserverport = 587
smtppass = XXXXXX

The above example is for my gmail id configuration to git. Change my email ids and names with yours, change smtp server if you are using an email other then gmail, but Heay ! who doesn’t have gmail id ? keep it simple :). And yeah don’t forget to write your actual gmail login password in place of XXXXXX, yes that’s right you heard it correct 😉

    4) Enable access for less secure apps in gmail : This is a very important step, otherwise gmail will not let you send your mails via git email client. So, to do this log in to your gmail id, and in same browser enter the below url.

https://www.google.com/settings/security/lesssecureapps

It will show the option to ‘Turn on’ your access of less secure apps, select that !


II. Download Linux kernel


Go to your home directory, or any folder where you want to keep your linux kernel repository, run the below commands

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

This will take some time, go watch some movie and come back, don’t worry I will be waiting here.


 III. Creating a Patch


You have your changes ready ? in case you are not sure what you need to submit go to section V to know how all you begin to create the patches for contributing to linux kernel.

Say, you want to change the file ” drivers/staging/iio/iio_simple_dummy_buffer.c”

1 ) Do your changes in file and save it

vim drivers/staging/iio/iio_simple_dummy_buffer.c

2) Add the changes to your git

git add drivers/staging/iio/iio_simple_dummy_buffer.c

3) Commit your changes

git commit -s -v

You need to add a subject and description of changes you have made in patch. Do a git log of the file you have changed in order to get the format of subject and description for that particular file

Thing to make sure is that your sign-off tag should be there in this patch, anyway -s argument of above command will take care of it don’t worry.

4) Whom to send : Get the maintainers email ids by running get_maintainer script on your patch or file you changed

./script/get_maintainer.pl -f  drivers/staging/iio/iio_simple_dummy_buffer.c

./script/get_maintainer.pl –patch /tmp/0001-test.patch

copy these mail ids

And next section will be about sending your patch to linux community, but before that make sure your changes doesn’t have any coding style issues, this can be checked by script ./script/checkpatch.pl

./script/checkpatch.pl -f drivers/staging/iio/iio_simple_dummy_buffer.c

./script/checkpatch.pl –patch /tmp/0001-test.patch

 


IV. Sending The Patch


This is easy

git send-email –annotate HEAD^1

it will prompt for email ids whom to sent, paste the email ids copied “whom to send” section. It may also ask for ‘in reply to’ you can just avoid it by pressing enter for first patch as this option is for replying on an existing mail chain. Then for final confirmation it will ask, press ‘y’ … hit enter …. and YOU ARE DONE !! 🙂

After some 10 -15 minutes you can see your patch in lkml.org site, good start :). And if every thing goes fine soon your patch will be merged in mainline. Depending on the complexity of patch and availability of maintainers your patch can be given feed back ranging from 1 minute to 20 days. In case you don’t get any feedback ping them back, but at least give around 20 days to Linux Kernel Maintainers to respond.


V. What to submit


I would suggest to start your patch submission from driver/staging directory, until you are confident enough on how to send patches.

You can run some static analyzer tool in order to fix there errors, but keep in mind that there could be many false positives too by these tools, so be careful !

Run Checkpatch.pl on kernel, and fix the errors/warnings reported by it, these are many.

Run coccicheck: http://coccinelle.lip6.fr/

After that you gan grep for “TODO” and “FIXME” in kernel code, and can implement the missing logic, be careful here.

And if you are confident in some module or framework of linux kernel, go review them and in case you feel you can make them better go ahead and do the changes.

And as the last advice I can give you while interacting with Linux kernel developers in lkml, be patient and be humble 🙂

Get your hands dirty and let me know if you need any help in comments section.

 

Hope this helps,

Saurabh Singh Sengar

email-to : saurabh.truth@gmail.com