By Jonathan Corbet
June 21, 2011
The
first part of this pair of articles
described the kernel's mechanism for dealing with non-discoverable devices:
platform devices. The platform device scheme has a long history and is
heavily used, but it has some disadvantages, the biggest of which is the
need to instantiate these devices in code. There are alternatives coming
into play, though; this article will describe how platform devices interact
with the device tree mechanism.
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.
(
Log in to post comments)