Sebastien, in a sense you are right about drivers but only in part.
There is a main issue with switching to data driven firmware
configuration approach (ie a device tree) in that it renders common
methods of getting rid of unused code not as effective anymore (still
usable though but with certain considerations). For example, using
-ffunction-sections and -fdata-sections along with --gc-sections will
not work as cleanly anymore as it does with completely preprocessor
driven configuration approach.
However, you are not correct when you say ALL drivers need to be
included. Instead of managing what gets included in the firmware at a
function level, one switches to doing it on driver level or file
level. The -ffunction-sections/-fdata-sections options will still work
as means of code cleanup, with the only difference that in order to
make the most of these options, one would need to make an extra effort
to ensure that no drivers that are not used are included in the build.
I'm using device tree today quite successfully with our FreeRTOS based
firmwares. What I have done is greatly simplify how the device tree is
used. I compile the device tree into a blob which gets written out as
a generated C file which contains a uint8_t array with the compiled
device tree blob. I then have macros defined which automatically
register drivers into a global linked list of all drivers that are
compiled into the firmware (similar to linux but just a lot more
simplified). Then during boot the user firmware calls
"probe_device_drivers()" with the device tree blob as argument.
There are numerous benefits that are very attractive with this
approach, at least in my current line of work:
- Device tree defines the init order of device drivers. Much better
than doing it in code.
- Device tree defines links between instances of devices. Ie if you
want your rfid driver to use main uart nr 1 or nr 2 as way of talking
to your reader, you set this in device tree and not in code. Also any
baud rate settings are defined in the device tree.
- Device tree allows all low level drivers to be the same for all
applications (ie "leds" driver doesn't need to change for
applications. Not even in the preprocessor sense where the written
code is the same but compiled code can have different meaning
depending on configuration it is compiled for. Nothing like that).
Device tree easily defines "references" from one device to another.
Simplest example is again led pins. All pinmux settings are defined in
one section containing only led pins and then leds section has a
reference to the pins. Application simply queries main led indicator
interface and manipulates abstract leds 1,2,3..etc. The mapping of
these leds to hardware is defined in the device tree. Very clean
approach that removes all need to modify the driver code for any
application.
- Device tree ensure that all settings are in one place, ie the device
tree file. This makes porting the code to a new board very
straightforward. Even compilation against linux targets is as simple
as supplying a new device tree which instead of defining hardware
settings, define tty settings and settings for various emulated
devices such as vcan instead of hardware can.
- Device tree defines a primitive form of devfs with same structure
for an array of applications. Ie the application can "open" devices
using paths in the device tree. For example,
serial_find("/serial/debug") returns an abstract serial interface that
points to the debug port. Which uart this actually ends up on is
configured in the device tree which corresponds to the board on which
the application will run.
- Device tree can be as small or big as your application desires. If
you only have a small number of devices to define then device tree
will be very small. Compiled binary device tree blob is quite
efficient storage as well. And the small amount of extra flash storage
that it takes up is a good price to pay for the flexibility it offers,
in my opinion.
I also heavily use c preprocessor in device tree. Some linux device
trees use preprocessor, others don't. I like using preprocessor
because it allows the device tree to include human readable bit
combinations that are easy to interpret. I took the preprocessor step
from linux and I just use it before compiling the device tree blob
with dtc and dumping it into hex string.
Libfdt is what I use for parsing the device tree.
Attached is a small example from an stm32 can driver I wrote which
reads it's settings from the device tree node that corresponds to the
device the driver is going to control. I'm very happy with how device
tree turned out for current project that I'm working on and will
probably never go back to the old approach of doing all of this using
ifdefs in code.