Platform devices and device trees
The current platform device mechanism is relatively easy to use for a developer trying to bring up Linux on a new system. It's just a matter of creating the descriptions for the devices present on that system and registering all of the devices at boot time. Unfortunately, this approach leads to the proliferation of "board files," each of which describes a single type of computer. Kernels are typically built around a single board file and cannot boot on any other type of system. Board files sort of worked when there were relatively small numbers of embedded system types to deal with. Now Linux-based embedded systems are everywhere, architectures which have typically depended on board files (ARM, in particular) are finding their way into more types of systems, and the whole scheme looks poised to collapse under its own weight.
The hoped-for solution to this problem goes by the term "device trees"; in essence, a device tree is a textual description of a specific system's hardware configuration. The device tree is passed to the kernel at boot time; the kernel then reads through it to learn about what kind of system it is actually running on. With luck, device trees will abstract the differences between systems into boot-time data and allow generic kernels to run on a much wider variety of hardware.
This article is a good introduction to the device tree format and how it can be used to describe real-world systems; it is recommended reading for anybody interested in the subject.
It is possible for platform devices to work on a device-tree-enabled system with no extra work at all, especially once Grant Likely's improvements are merged. If the device tree includes a platform device (where such devices, in the device tree context, are those which are direct children of the root or are attached to a "simple bus"), that device will be instantiated and matched against a driver. The memory-mapped I/O and interrupt resources will be marshalled from the device tree description and made available to the device's probe() function in the usual way. The driver need not know that the device was instantiated out of a device tree rather than from a hard-coded platform device definition.
Life is not always quite that simple, though. Device names appearing in the device tree (in the "compatible" property) tend to take a standardized form which does not necessarily match the name given to the driver in the Linux kernel; among other things, device trees really are meant to work with more than one operating system. So it may be desirable to attach specific names to a platform device for use with device trees. The kernel provides an of_device_id structure which can be used for this purpose:
static const struct of_device_id my_of_ids[] = { { .compatible = "long,funky-device-tree-name" }, { } };
When the platform driver is declared, it stores a pointer to this table in the driver substructure:
static struct platform_driver my_driver = { /* ... */ .driver = { .name = "my-driver", .of_match_table = my_of_ids } };
The driver can also declare the ID table as a device table to enable autoloading of the module as the device tree is instantiated:
MODULE_DEVICE_TABLE(of, my_of_ids);
The one other thing capable of complicating the situation is platform data. Needless to say, the device tree code is unaware of the specific structure used by a given driver for its platform data, so it will be unable to provide that information in that form. On the other hand, the device tree mechanism is equipped to allow the passing of just about any information that the driver may need to know. Making use of that information will require the driver to become a bit more aware of the device tree subsystem, though.
Drivers expecting platform data should check the dev.platform_data pointer in the usual way. If there is a non-null value there, the driver has been instantiated in the traditional way and device tree does not enter into the picture; the platform data should be used in the usual way. If, however, the driver has been instantiated from the device tree code, the platform_data pointer will be null, indicating that the information must be acquired from the device tree directly.
In this case, the driver will find a device_node pointer in the platform devices dev.of_node field. The various device tree access functions (of_get_property(), primarily) can then be used to extract the needed information from the device tree. After that, it's business as usual.
In summary: making platform drivers work with device trees is a relatively
straightforward task. It is mostly a matter of getting the right names in
place so that the binding between a device tree node and the driver can be
made, with a bit of additional work required in cases where platform data
is in use. The nice result is that the static platform_device
declarations can go away, along with the board files that contain them.
That should, eventually, allow the removal of a bunch of boilerplate code
from the kernel while simultaneously making the kernel more flexible.
Index entries for this article | |
---|---|
Kernel | Device tree |
Kernel | Platform data |
Posted Jun 23, 2011 3:54 UTC (Thu)
by neilbrown (subscriber, #359)
[Link] (1 responses)
I thought "it is a bit odd starting an identifier with a preposition" .... but then the answer floated up from long distant memories: "Open Firmware" - it isn't a preposition at all, it is an abbreviation!
Posted Jun 23, 2011 22:26 UTC (Thu)
by glikely (subscriber, #39601)
[Link]
Posted Jun 23, 2011 13:10 UTC (Thu)
by etienne (guest, #25256)
[Link] (12 responses)
One way to solve the problem may be to parse-check and insert a section of the ELF file to represent the device tree, but I am not sure if it is more interesting to tell the linker that "this device is not present, that device is at address 0x333444, ..." so that the linker can remove parts of code and optimise others.
Another way to handle the problem would be to have a device tree checker in "Das U-boot", but I am not sure we want to complexify the boot code because it is difficult to handle errors at this point (run a text editor?).
Moreover, boards with system-on-chip processor have a constant part due to the system-on-chip used, and it would probably be better to keep the system-on-chip description (big but limited number) separate of the one of the board (unlimited).
Just my £0.02...
Posted Jun 23, 2011 15:14 UTC (Thu)
by krisis (guest, #70792)
[Link] (9 responses)
Posted Jun 23, 2011 19:33 UTC (Thu)
by glikely (subscriber, #39601)
[Link] (8 responses)
Posted Jun 23, 2011 22:07 UTC (Thu)
by krisis (guest, #70792)
[Link] (7 responses)
I work on an embedded platform with a DM9000 ethernet controller. This particular controller can either get its MAC address from an attached EEPROM or it can be set via the platform_data mechanism. As a cost saving measure, there is no EEPROM attached to the controller on this board, so in order to get a MAC address, we pull the die ID from the CPU during boot and use that to initialize the MAC address in the device struct before we pass it to the driver core.
Is there a mechanism to enable these sort of hacks with device trees and if so, how?
Posted Jun 23, 2011 22:11 UTC (Thu)
by glikely (subscriber, #39601)
[Link] (5 responses)
Posted Jun 23, 2011 22:28 UTC (Thu)
by krisis (guest, #70792)
[Link] (4 responses)
The hack is that I'm getting a unique MAC address for each of the boards by using the unique-per-cpu die ID, which I pull during board setup.
(The code for this hack is in arch/arm/mach-omap2/board-devkit8000.c if anyone wants to have a look. It's the omap_dm9000_init function)
Posted Jun 23, 2011 22:37 UTC (Thu)
by glikely (subscriber, #39601)
[Link] (3 responses)
The model pretty much assumes that the DT data is complete and accurate by the time it is passed to the kernel. If you need to do machine-specific hacks, there is little recourse but but to have machine specific setup code. DT doesn't change the situation in that regard. If really necessary, there is a way to attach supplemental platform_data to devices registered from the DT, but it is discouraged. However, the DT can be dynamically modified to squirt in the mac address either by the boot loader (U-Boot) or when the .dtb is installed on the board. You don't need to have a separate .dts for each board with a different MAC address.
Posted Jun 24, 2011 0:28 UTC (Fri)
by dlang (guest, #313)
[Link] (2 responses)
Posted Jun 24, 2011 18:47 UTC (Fri)
by broonie (subscriber, #7078)
[Link] (1 responses)
Posted Jun 24, 2011 22:17 UTC (Fri)
by dlang (guest, #313)
[Link]
yes, it would be nice for the code to look in the device tree to see if there is a MAC address provided.
but if there isn't, he shouldn't have special case code in that one driver to calculate the MAC, he should call the general case code to handle the case where there is no MAC (and come to think of it, that general case code should probably be what is taught to look in the device tree for the MAC)
Posted Jun 24, 2011 11:13 UTC (Fri)
by etienne (guest, #25256)
[Link]
Posted Jun 23, 2011 20:04 UTC (Thu)
by klossner (subscriber, #30046)
[Link]
My current work is with products that use u-boot as the bootloader. u-boot takes the binary device tree, adds decorations to reflect the hardware (e.g., describing the amount of installed system RAM), then passes it to the Linux kernel.
It's true that a single system-on-chip processor has a constant part. But it may be one of a large family of such chips. It can be easier to describe the individual components of the SOC in the device tree (e.g., there are PCI Express ports at addresses X and Y and an Ethernet port at Z) than to prepare a code module for each chip.
Posted Jun 23, 2011 20:53 UTC (Thu)
by glikely (subscriber, #39601)
[Link]
The device tree compiler (dtc) does some semantic checking. It is limited, but adding more semantic checking is definitely welcome and encouraged. At least on ARM, if you're debugging, the low level debug code can select an output device at kernel build time and get output even in the startup assembly code. Well before even looking at the DT data. Not sure what the argument is here. Yes, out of tree code is often filled with unconventional hacks that don't survive by the time they hit mainline. However, once DT bindings are mainlined, we try *really hard* not to break them. On of the requirements for merging DT parsing code is that it must also document any new bindings in the Documentation directory. The device tree compiler has an include mechanism so that multiple board .dts files can include a single SoC .dts description. The SoC description is indeed static, and any number of boards can be build off of it. I did consider a scheme of passing multiple dtbs to the kernel, one for configuration, one for board layout, one for SoC layout, etc, but figuring out how to get the kernel to manage the separated blobs was worse overall. Instead, it is handled in the dts source form and merged into a single dtb by dtc.
Posted Jun 24, 2011 18:58 UTC (Fri)
by giraffedata (guest, #1954)
[Link]
I believe those are names of device types, not of devices.
It's important not to confuse the object with the class.
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
- there does not seem to be any "device trees" checker, so that errors are found at execution time and not compilation/link time.
- the kernel, at the time it is analysing the "device trees", is still looking for its serial port or permanent storage, so it cannot display or store any kind of error/warning message.
- in an area where kernels are often seriously patched (embedded), a construct may be valid for a stock kernel but not parse-able by a modified kernel.
You still can have a single pre-linked kernel in this case, it just has some relocation to sort out (and relocation to NULL could be handled specially by the linker) - but still a single kernel.
There isn't any program run (like LILO or GRUB config manager) when the boot configuration is changed so no device tree check there.
Then, we could even dream of the system on chip provider giving a complete and valid description of their product as open source...
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
Wouldn't that just set the MAC address to a fixed value for all of my boards?
The hack is that I'm getting a unique MAC address for each of the boards by using the unique-per-cpu die ID, which I pull during board setup.
(The code for this hack is in arch/arm/mach-omap2/board-devkit8000.c if anyone wants to have a look. It's the omap_dm9000_init function)
Nice. I have a devkit8000, but I could never get a modern kernel to boot on the thing.
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
Platform devices and device trees
Last time it happened in the PPC world was when the device tree file was not correctly loaded in memory or loaded to the wrong RAM address/from the wrong FLASH address or corrupted.
I have read here on LVN about a patch to insert the device tree in the kernel file, so that would sort out this kind of problem.
I am still puzzled by the fact that the ARM processor I am using is completely defined, all its device address are fixed - up to the point that I have a CPU choice in the kernel menuconfig (CONFIG_ARCH_TI816X=y), yet the future would be a kernel file with variable device addresses to be resolved at run time from another file. (resolving at link time would be fine)
Now I shut up, my current problem is obviously not device tree - I have to look again at u-boot config.
Platform devices and device trees
Platform devices and device trees
- there does not seem to be any "device trees" checker, so that errors are found at execution time and not compilation/link time.
- the kernel, at the time it is analysing the "device trees", is still looking for its serial port or permanent storage, so it cannot display or store any kind of error/warning message.
- in an area where kernels are often seriously patched (embedded), a construct may be valid for a stock kernel but not parse-able by a modified kernel.
Moreover, boards with system-on-chip processor have a constant part due to the system-on-chip used, and it would probably be better to keep the system-on-chip description (big but limited number) separate of the one of the board (unlimited).
Platform devices and device trees
Device names appearing in the device tree (in the "compatible" property)