Reputation: 2734
I'm trying to learn how device tree
works. I have read some documentation and more or less understand what are dts
, dtsi
and dtb
files.
What I don't understand very well is how the information in the dtb
files is accessed from the final application. I have been reading the zephyr
os documentation and a bit of the linux kernel to understand this, but it's still not very clear to me.
What I have understood for now is that the final application (possibly the OS kernel) reads and parses the dtb
files 'manually'. This means that each OS kernel will parse the dtb
files in its own way, is this correct?.
However, as far as I understand, the application is not linked against the dtb
file, the latter is flashed in some memory area and then the app. access it somehow. Then, how can the application be built if those symbols are not available at build time?
Upvotes: 1
Views: 2305
Reputation: 5895
Disclaimer: I am not a Linux Kernel expert.
Your question was answered here I guess. Yes, each OS kernel will parse the DTB file in its own way. In the case of Linux, the DTB would for example be read by u-boot, and u-boot would start the Linux kernel and pass the address where it loaded the DTB BLOB.
In the example hereafter, u-boot would load the kernel and the DTB into memory from a FAT filesystem then transfer the control to the kernel at address 0x80300000 while telling it its DTB BLOB will be available at address 0x815f0000:
fatload mmc 0:1 0x80300000 zImage
fatload mmc 0:1 0x815f0000 beagle-xm.dtb
bootz 0x80300000 - 0x815f0000
The DTB BLOB does contain both symbol names and their associated values. The application will use some code specialized in reading the DTB format, such as libfdt, for retrieving a value from its symbol name. It does not need to now about the DTB content at the time it is being built. This does allow the same compiled kernel to run unmodified on different hardwares, the various hardware capabilities being described in the DTB.
In order for an Operating System to access the information contained in the DTB efficiently, the information will be extracted from the DTB BLOB once using libfdt, then be put into an efficient data structure. i.e. transformed from the 'flatened' format into the 'expanded' one for example.
I would strongly suggest to read:
Note that the Zephyr approach is not standard: when the Open Firmware/Device Tree scheme was designed for allowing the 'late binding' of the application and the hardware description, they opted for a more static approach instead.
Zephyr is actually using a custom tool that does process the DTB file and generates C header files containing the same information in the form of macros respecting certain naming conventions. This may be why you are confused regarding the exact stage where the DTB content and the application using it will be bound together.
Don't try ramping-up on the Device Tree concepts using Zephyr. Besides, their tool is still far from perfect and chokes when processing perfectly valid DTB files.
One extract of a Linux kernel driver code binding the DTB to a driver:
static const struct platform_device_id mxs_auart_devtype[] = {
{ .name = "mxs-auart-imx23", .driver_data = IMX23_AUART },
{ .name = "mxs-auart-imx28", .driver_data = IMX28_AUART },
{ .name = "as-auart-asm9260", .driver_data = ASM9260_AUART },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, mxs_auart_devtype);
static const struct of_device_id mxs_auart_dt_ids[] = {
{
.compatible = "fsl,imx28-auart",
.data = &mxs_auart_devtype[IMX28_AUART]
}, {
.compatible = "fsl,imx23-auart",
.data = &mxs_auart_devtype[IMX23_AUART]
}, {
.compatible = "alphascale,asm9260-auart",
.data = &mxs_auart_devtype[ASM9260_AUART]
}, { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids);
An equivalent (different driver though) found in Zephyr code:
#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm5), nxp_lpc_spi, okay)
/* Attach 12 MHz clock to FLEXCOMM5 */
CLOCK_AttachClk(kFRO_HF_to_FLEXCOMM5);
/* reset FLEXCOMM for SPI */
RESET_PeripheralReset(kFC5_RST_SHIFT_RSTn);
#endif
Upvotes: 1