Martin L Roth has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/63977 )
Change subject: Documentation/internals: Add devicetree documentation ......................................................................
Documentation/internals: Add devicetree documentation
Signed-off-by: Martin Roth gaumless@gmail.com Change-Id: I2a43a96911844bd2b682004d5423126ad00a4bf3 --- A Documentation/internals/devicetree.md 1 file changed, 621 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/77/63977/1
diff --git a/Documentation/internals/devicetree.md b/Documentation/internals/devicetree.md new file mode 100644 index 0000000..c1c7cd5 --- /dev/null +++ b/Documentation/internals/devicetree.md @@ -0,0 +1,621 @@ + +# Devicetree + +## Introduction to the coreboot devicetree + +The first thing that may come to mind when one hears “DeviceTree" is a +different sort of description file that is generally passed to the Linux +kernel to describe a system’s components. Both that devicetree and +coreboot’s devicetree serve fundamentally the same purpose, but are +otherwise unrelated and have completely different syntax. The term +devicetree was used long before either version was created, and was +initially used in coreboot as a generic term. + +coreboot's devicetree’s main use is to define and describe the runtime +configuration and settings of the hardware on a board, chip or device +level. It defines which of the functions of the chips on the board are +enabled, and how they’re configured. + +The devicetree file is parsed during the build process by a utility +named sconfig, which translates the devicetree into a tree of C +structures containing the included devices. This code is placed in the +file static.c and a couple of header files all under the build +directory. This file is then built into the binaries for the various +coreboot stages and is referred to during the coreboot boot process. + +For the early stages of the coreboot boot process, the data that is +generated by sconfig is a useful resource, but this structure is the +critical architectural glue of ramstage. This structure gets filled in +with pointers to every chip's initialization code, allowing ramstage to +find and initialize those devices through the chip_operations +structures. + + +### History of coreboot’s devicetree + +The initial devicetree in coreboot was introduced in 2003 by Ron Minnich +as a part of the linuxbios config file, 'config.lb'. At this point both +the devicetree and config options were in the same file. In 2009, +when Kconfig was added into the coreboot build, devicetree was split +out into its own file for each mainboard in a commit with this message: + +```text +devicetree.cb + +The devicetree that formerly resided in src/mainboard/*/*/Config.lb. + +Just without the build system crap +``` + +The devicetree structure was initially mainly used only in ramstage for +PCI device enumeration, configuration and resource allocation. It has +since expanded for use in the pre-ram stages as a read-only structure. + +The language used in the devicetree has been expanded greatly since it +was first introduced as well, adding new features every year or so. + + +### Devicetree Registers + +In coreboot, the devicetree register setting is one of the two main +methods used to configure a board’s properties. In this way, devicetree +is similar in function to Kconfig. It’s more flexible in many ways as +it can specify not only single values, but also arrays or structures. +It's also even more static than Kconfig because there’s no update +mechanism for it other than editing the devicetree files. + + +### Adding new static configuration options: Devicetree or Kconfig + +When adding options for a new board or chip, there is frequently a +decision that needs to be made in terms of how the option should be +added. Using the devicetree or Kconfig are the two typical methods of +build-time configuration. Below are some general rules of when each one +should be used. + +Kconfig should be used if the option is used to configure the build in a +Makefile, or if the option is something that should be user selectable. +Kconfig is also preferred if the configuration is a global option and +not limited to a single chip. Another thing that Kconfig is good at is +handling decisions based on other configuration options, which isn’t +something that devicetree can really do. + +Devicetree should obviously be used to define the configuration of the +hardware hierarchy, but it's also preferred if the option is only used +in the C code and is static for a mainboard. or if the option is chip +specific. As mentioned earlier, Devicetree registers can also be used +to create structures or arrays that Kconfig can't. + +Both Kconfig and devicetree can be used in the C code for runtime +configuration, but there’s a huge difference on the back-end about how +these are handled. Because Kconfig generates a #define of the choice, +the compiler can eliminate any code paths not used by the option. +Devicetree options, on the other hand, are actual runtime selections and +the code for any and all choices will remain in the final build. + + +## Three levels of devicetree files + +There are currently three different levels of devicetrees used to build +up the structure of components and register values in coreboot. From +the lowest, most general level to the highest and most specific, they +are chipset.cb, devicetree.cb and overridetree.cb. + +Unless there’s a specific reason to name them something other than these +names, they are the names that should be used. + +For newer SoCs and chipset, there will generally be a chipset.cb file, +and every mainboard is required to have a devicetree.cb file, although +it can be empty if somehow everything is the same as the SoC level file. +An overridetree.cb file is only required if variants have differences +from the devicetree at the primary mainboard level. + + +### SoC / chipset level, chipset.cb + +The chipset.cb file was added in October of 2020 allowing a single +chipset or SoC to provide a "base level" devicetree, cutting down on +duplication between mainboards. + +The chipset.cb file also typically will define human readable "aliases" +for particular devices so that mainboards can use those instead of just +the PCI routing numbers and the like. + +The use of the chipset.cb file is specified in Kconfig by the symbol +CONFIG_CHIPSET_DEVICETREE. + +In chipset.cb file, you might see a couple of lines like this: + +```text +device pci 17.0 alias sata off end +device pci 1e.0 alias uart0 off end +``` + +This sets the aliases for these PCI devices and default them to being +disabled. + + +### Primary mainboard level, devicetree.cb + +Each mainboard must have a devicetree.cb file. The filename and path is +set to a default value of `src/mainboard/<VENDOR><MBNAME>/devicetree` by +the CONFIG_DEVICETREE symbol in Kconfig. + +If a mainboard and variants typically wanted both devices enabled, +you’d see the following in devicetree.cb: + +```text +device ref sata on end +device ref uart0 on end +``` + +### Mainboard variant level, overrridetree.cb + +Introduced in 2018 to reduce duplication and maintenance of variant +boards, the overridetree.cb file is the most specific level of +devicetree file. + +This allows for a base devicetree at the top mainboard level that is +shared by all variants, and each variant only needs to update for their +specific values + +The override tree filename is set in Kconfig with the +OVERRIDE_DEVICETREE symbol and is typically named "overridetree.cb". + +Finally, if one of the variants of a mainboard came without a sata +connector, it could again disable sata with the following in +overridetree.cb: + +```text + device ref sata on end +``` + +## Additional files + + +### chip.h files + +coreboot looks at the chip as a collection of devices. That collection +can be a single device or multiple different devices. The ‘chip’ +keyword which will be described more later, starts this collection of +devices. Following the ‘chip’ keyword is a directory name which +contains the code to deal with that collection of devices. There may +optionally be a chip.h file in that directory which will also be parsed +to create the devicetree data structures. + +If the chip.h file is present in that directory, it is used to define a +structure containing the "register definitions" for the chip. The +values for this structure's members get set in one of the devicetree +files. If not specifically set in one of the devicetree files, the +value of the register defaults to 0. The chip.h file frequently also +contains macros, enums and sub-structures used to set the register +structure's members. + +The structure for the chip’s register definition is named after the +directory that would contains the chip.h file, with the slashes changed +to underscores, then appended by ‘_config’. The src/ directory is +omitted. + +This means that a line in a devicetree file containing +`chip drivers/i2c/hid` would use `src/drivers/i2c/hid/chip.h`, and the +register definition structure it contains would be named +`drivers_i2c_hid_config`. + + +Here is the contents of that chip.h file: + +```text +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRIVERS_I2C_HID_CHIP_H__ +#define __DRIVERS_I2C_HID_CHIP_H__ +#include <drivers/i2c/generic/chip.h> +#define I2C_HID_CID "PNP0C50" + +struct drivers_i2c_hid_config { + struct drivers_i2c_generic_config generic; + uint8_t hid_desc_reg_offset; +}; + +#endif /* __I2C_HID_CHIP_H__ */ +``` + +## The sconfig utility and generated files + + +### coreboot/util/sconfig + +Sconfig is the tool that parses the coreboot devicetrees and turns them +into a collection of C style structures. This is a coreboot specific +tool, compiled with flex & bison to define and parse the domain specific +language used by coreboot’s device + +Sconfig is called by the makefile during the build process, and +shouldn’t generally need to be run directly. Despite that, it obviously +can be run manually if required, and gives the basic command line +options used to control it. + +```text +usage: sconfig <options> + + -c | --output_c : Path to output static.c file (required) + -r | --output_h : Path to header static.h file (required) + -d | --output_d : Path to header static_devices.h file (required) + -f | --output_f : Path to header static_fw_config.h file (required) + -m | --mainboard_devtree : Path to mainboard devicetree file (required) + -o | --override_devtree : Path to override devicetree file (optional) + -p | --chipset_devtree : Path to chipset/SOC devicetree file (optional) +``` + +### sconfig inputs + +The sconfig input files chip.h, chipset.cb, devicetree.cb and +overridetree.cb were discussed previously. As the usage above shows, +the only required input file is the mainboard devicetree. The additional +devicetree files, chipset.cb and overridetree.cb are optional, and the +chip.h files do not need to be specified, because their location is +contained inside the files + +Constructing the devicetree input files will be discussed later. + + +### sconfig outputs + +#### static.c + +This file is the primary file generated by sconfig. It contains the +main data about the component busses and interfaces. + +For historic reasons, static.c is generated in the +`build/mainboard/<VENDOR>/<MBNAME> directory` + + +#### static.h + +The static.h file is the main header file included by most coreboot +files that need to use the devicetree data. This is included by +src/include/device/device.h, which contains all of the definitions, +structures, and function prototypes for dealing with the devicetree +generated output. + +Static.h used to contain all of the output directly, but as of October +2020, simply contains include statements bringing in the other two +generated header files. This allows the two headers to be included +separately so that the firmware config options can be used in a payload. + + +#### static_devices.h + +The file static_devices.h contains extern declarations of all of the +device structures defined in static.c. + + +#### static_fw_config.h + +static_fw_config.h contains the FW_CONFIG_FIELD_* macros only, which +makes it easily consumable by a payload or anything else which wishes to +use the platform’s FW_CONFIG fields. + +## Devicetree Example + + +### A very simple devicetree + +This is the devicetree.cb file in src/mainboard/sifive/hifive-unleashed +with line numbers added + +Non-X86 devicetree files tend to be very simple like this. + +```text + 1 # SPDX-License-Identifier: GPL-2.0-only + 2 chip soc/sifive/fu540 + 3 device cpu_cluster 0 on end + 4 end +``` + +This can be broken down as follows: + +Line 1: Comments start with the '#' character. This line is what’s +known as the SPDX header, which identifies how this particular file is +licensed. + +Line 2: "chip" starts a section for a collection of devices. It is +followed by a directory which contains the code to an optional chip.h +file. + +Line 3: "device" starts a device block, but needs to be followed by a +device type. "cpu_cluster" is the type, and this needs to be followed +by an identifier - "0". + +The device can be marked enabled "on", or disabled "off", then the device +block is closed with the "end" keyword. + +Line 4: Finally "end" closes the block started by the "chip" keyword. + + +### Generated files + +Continuing with the simple sifive/hifive-unleashed mainboard’s +devicetree, these are the files generated from the devicetree as of +2022-05-01. Because the devicetree is so simple, there’s almost nothing +in the header files + + +#### build/static.h + +```C +#ifndef __STATIC_DEVICE_TREE_H +#define __STATIC_DEVICE_TREE_H + +#include <static_fw_config.h> +#include <static_devices.h> + +#endif /* __STATIC_DEVICE_TREE_H */ +``` + + + +#### build/static_devices.h + +```C +#ifndef __STATIC_DEVICES_H +#define __STATIC_DEVICES_H +#include <device/device.h> +/* expose_device_names */ +#endif /* __STATIC_DEVICE_NAMES_H */ +``` + +#### build/static_fw_config.h + +Because this file doesn’t use any firmware config definitions in the +devicetree, there are also none here. + +```C +#ifndef __STATIC_FW_CONFIG_H +#define __STATIC_FW_CONFIG_H +#endif /* __STATIC_FW_CONFIG_H */ +``` + +#### build/mainboard/sifive/hifive-unleashed/static.c + + +##### Includes + +```text +1 #include <boot/coreboot_tables.h> +2 #include <device/device.h> +3 #include <device/pci.h> +4 #include <fw_config.h> +5 #include <static.h> +``` + +Lines 1 - 5: Includes of header files required for the following +structures. + + +##### Declarations for chip-ops + +```text +6 +7 #if !DEVTREE_EARLY +8 __attribute__((weak)) struct chip_operations mainboard_ops = {}; +9 extern struct chip_operations soc_sifive_fu540_ops; +10 #endif +``` + +Lines 7 & 10: The ops structures inside this IF block are only used in +ramstage. + +Lines 8 - 9: Declarations for chip ops structures. This section will +expand as more chips are added to the device tree. + +* Line 8, for mainboard_ops, is always present, and doesn’t change between +boards. It’s defined as a weak function because the mainboard may or may +not define a chip_operations structure. + +* Line 9 is created by the chip declaration in the devicetree file. There +will be a similar line for every chip declared in the devicetree. + + +##### STORAGE definition + +```text +11 +12 #define STORAGE static __unused DEVTREE_CONST +``` + +Line 12: This macro resolves to ‘static __unused const’ in early stages +and ‘static __unused’ in ramstage. + + +##### Structure definitions + +```text +13 +14 +15 /* pass 0 */ +16 STORAGE struct bus dev_root_links[]; +17 STORAGE struct device _dev_0; +18 DEVTREE_CONST struct device * DEVTREE_CONST last_dev = &_dev_0; +``` + +Lines 16 - 18 declare all of the structures used in static.c + + +##### Register Structures + +```text +19 +20 /* chip configs */ +``` + +Line 20 is the start of an empty section for this mainboard, but it +would contain any chip register structures defined in any of the chip.h +files. + + +##### Dev_root structure + +Line 21 - Line 44: dev_root: This is the mainboard structure, and the +head of the linked list to all devices. This structure is created for +every mainboard regardless of anything else in the devicetree. Though +the mainboard is frequently listed in the devicetree, it does not need +to be specified to generate this structure. + +```text +21 +22 /* pass 1 */ +23 DEVTREE_CONST struct device dev_root = { +24 #if !DEVTREE_EARLY +25 .ops = &default_dev_ops_root, +26 #endif +27 .bus = &dev_root_links[0], +28 .path = { .type = DEVICE_PATH_ROOT }, +29 .enabled = 1, +30 .hidden = 0, +31 .mandatory = 0, +32 .on_mainboard = 1, +33 .link_list = &dev_root_links[0], +34 .sibling = NULL, +35 #if !DEVTREE_EARLY +36 .chip_ops = &mainboard_ops, +37 .name = mainboard_name, +38 #endif +39 .next=&_dev_0, +40 #if !DEVTREE_EARLY +41 #if CONFIG(GENERATE_SMBIOS_TABLES) +42 #endif +43 #endif +44 }; +``` + +Lines 24 - 26: This points to a default ramstage device_operation +structure (described later) in the file src/device/root_device.c which +does nothing by default. If you wish to use this device structure for +something (like generating mainboard specific acpi tables), it can be +updated early in ramstage in the mainboard’s +chip_operations::enable_dev() function, also described later. + +Line 27: The pointer to the root link of this bus. Because this device +is the root link of this bus, it’s pointing at its own bus structure. + +Lines 28 - 32: Enumeration Status. + +* The hidden and mandatory are only ever set by those keywords in the + devicetree. +* The enabled status is initially set by the devicetree, but can be + modified in ramstage if the device is not detected. +* The on_mainboard status flag is set for anything that is specified in + devicetree. If the device were found during enumeration, this flag + would not be set. + +Line 33: The pointer to the next link on this bus in the devicetree +list. In this case, the next link and the root link are the same. This +is described more in the next section. + +Line 34: Sibling devices or chips are those that are defined at the same +level in the devicetree file. Examples would be northbridge and +southbridge chips, or pci devices and their functions within a chip. +Nothing should be at the same level as the mainboard chip, so this +should always be NULL. + +Line 36: The pointer to the mainboard’s chip_operations structure. The +mainboard is special in that although it’s not actually a chip, it still +gets a chip_operations structure. This allows the mainboard to perform +any required actions at the same points in the boot flow that a chip +would be able to do. + +Line 37: The mainboard name. This is a global variable set at build +time, also in the file src/device/root_device.c. + + +##### Dev_root_links + +lines 45 - 52: The dev_root bus structure + +This is the structure that is linked to on lines 27 & 33 above. It +provides pointers to the current device, and to the next device in the +linked list. + +A new bus structure is created for each different device type in the +devicetree. The same bus struct can be shared for the same device +types, even in different chips so long as the bus is contained within +the same cpu_cluster or domain. + +Typically, single socket x86 boards will have four of these structures: + +* Mainboard - always dev_root_links +* CPU_Cluster - typically _dev_0_links +* PCI Bus, Domain 0 - typically _dev_1_links +* ISA/LPC/eSPI bus - _dev_X_links, where X is the bridge device on PCI + +```text +45 STORAGE struct bus dev_root_links[] = { +46 [0] = { +47 .link_num = 0, +48 .dev = &dev_root, +49 .children = &_dev_0, +50 .next = NULL, +51 }, +52 }; +``` + +Line 47: .link: The index of this link. + +Line 48: .dev: Pointer to the current bridge device structure. + +Line 49: .children: The top device structure on the bus behind this +bridge device. + +Line 50: .next: The next bridge device on the current bus. + + +##### _dev_0 + +Line 53 - Line 72: The cpu_cluster device + +```text +53 STORAGE struct device _dev_0 = { +54 #if !DEVTREE_EARLY +55 .ops = NULL, +56 #endif +57 .bus = &dev_root_links[0], +58 .path = {.type=DEVICE_PATH_CPU_CLUSTER,{.cpu_cluster={ .cluster = 0x0 }}}, +59 .enabled = 1, +60 .hidden = 0, +61 .mandatory = 0, +62 .on_mainboard = 1, +63 .link_list = NULL, +64 .sibling = NULL, +65 #if !DEVTREE_EARLY +66 .chip_ops = &soc_sifive_fu540_ops, +67 #endif +68 #if !DEVTREE_EARLY +69 #if CONFIG(GENERATE_SMBIOS_TABLES) +70 #endif +71 #endif +72 }; +``` + +Lines 54-56: Pointer to the device_operations structure. Since this is +a chip, not a device, this pointer is NULL. + +Line 57: Pointer to the root link of this bus. Because this device is +directly under the mainboard, this points to the mainboard’s bus link. + +Line 58: The device_path structure, defined in src/include/device/path.h +is a path-type-specific structure detailing the identity of the device. +These should be unique in the devicetree. This is the field that’s +referenced when searching the devicetree for a device. + +Lines 59 - 62: Enumeration Status. Same as in dev_root + +Lines 63 & 64: Link list pointers to other devices in the tree. These +are NULL in this case because the only device in the devicetree that +generated this structure is the SoC chip. + +Lines 65 - 67: Pointer to the processor chip_ops structure. This is +only used in ramstage. + +Lines 68 - 71: Empty here, but this is where anything defined by +smbios_dev_info or smbios_slot_desc would go.