Skip to content

Commit

Permalink
dts: bindings: Add lots more documentation
Browse files Browse the repository at this point in the history
Add some information that would've saved me a lot of time:

 - Give an overview of how device tree nodes and bindings fit together,
   with examples. Assume people might be coming at it without knowing
   anything about device tree.

 - Explain how 'inherits' works in more detail

 - Explain what 'parent/child: bus: ...' does more concretely, and what
   problem it solves

 - Add more examples to show what things look like in the .dts file

 - Clean up the language a bit and make things more consistent

Also fix some errors, like 'properties: compatible: ...' being wrong
(missing 'constraint:' and compatible strings in the wrong place).

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
  • Loading branch information
ulfalizer authored and galak committed Jul 30, 2019
1 parent 20ccdab commit 850e362
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 81 deletions.
79 changes: 54 additions & 25 deletions doc/guides/dts/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,19 @@ The device tree files are compiled using the device tree compiler. The compiler
runs the .dts file through the C preprocessor to resolve any macro or #defines
utilized in the file. The output of the compile is another dts formatted file.

After compilation, a python script extracts information from the compiled device
tree file using a set of rules specified in YAML binding files. The extracted
information is placed in a header file that is used by the rest of the code as
the project is compiled.
After compilation, a Python script extracts information from the compiled
device tree file using rules specified in *bindings* (see the :ref:`bindings`
section). The extracted information is placed in a header file that is used by
the rest of the code as the project is compiled.

Temporary fixup files are required for device tree support on most devices.
These fixup files by default reside in the board and soc directories and are
named ``dts_fixup.h``. These fixup files map the generated include information to
the current driver/source usage.

The Python code that deals with device tree and bindings is in
:zephyr_file:`scripts/dts/`.

.. _dt_vs_kconfig:

Device Tree vs Kconfig
Expand Down Expand Up @@ -356,32 +359,58 @@ Example: Subset of DTS/YAML files for NXP FRDM K64F (Subject to Change)::
dts/bindings/pinctrl/nxp,kinetis-pinmux.yaml
dts/bindings/serial/nxp,kinetis-uart.yaml

YAML bindings for device nodes
******************************
.. _bindings:

Bindings
********

``.dts`` files describe the available hardware devices, but don't tell the
system which pieces of information are useful, or what kind of configuration
output (``#define``'s) should be generated. *Bindings* provide this
information. Bindings are files in YAML format.

Configuration output is only generated for devices that have bindings.

Nodes are mapped to bindings via their ``compatible`` string(s). Take
the following node as an example:

.. code-block:: none
bar-device {
compatible = "foo-company,bar-device";
...
};
This node would get mapped to a binding with this in it:

.. code-block:: yaml
...
properties:
compatible:
constraint: "foo-company,bar-device"
...
Device tree describes hardware and configuration, but it doesn't tell the
system which pieces of information are useful, or how to generate configuration
data from the device tree nodes. For this, we rely on YAML binding files to
describe the contents or definition of a device tree node and instruct how the
extracted information should be formatted.
Bindings are stored in :zephyr_file:`dts/bindings/`. The filename usually
matches the ``compatible`` string.

A YAML description (called "YAML binding") must be provided for every device node
that is a source of information for the system. A YAML binding file
is associated to each node ``compatible`` property. Information within the YAML
file will instruct the python DTS parsing script (located in ``scripts/dts``) how
each property of the node is expected to be generated, either the type of the
value or the format of its name. Node properties are generated as C-style
``#define``'s in include files
made available to all Zephyr components.
If a node has more than one ``compatible`` string, then the first binding found
is used, going from the first string to the last. For example, a node with
``compatible = "foo-company,bar-device", "generic-bar-device"`` would get
mapped to the binding for ``generic-bar-device`` if there is no binding for
``foo-company,bar-device``.

A YAML template file is provided to show the required format. This file is
located at::
If a node appears on a bus (e.g. I2C or SPI), then the bus type is also taken
into account when mapping nodes to bindings. See the description of ``parent``
and ``child`` in the template below.

dts/bindings/device_node.yaml.template
Below is a template that shows the format of binding files, stored in
:zephyr_file:`dts/bindings/device_node.yaml.template`.

YAML files must end in a .yaml suffix. YAML files are scanned during the
information extraction phase and are matched to device tree nodes via the
compatible property.
.. literalinclude:: ../../../dts/bindings/device_node.yaml.template
:language: yaml


Include files generation
Expand Down
128 changes: 72 additions & 56 deletions dts/bindings/device_node.yaml.template
Original file line number Diff line number Diff line change
@@ -1,88 +1,104 @@
title: <short description of the node>
title: Short description of the node

description: >
Longer free-form description of the node, with spanning
lines
Longer free-form description of the node.
Can go over multiple lines.

# Bindings are often based on other bindings, which are given in 'inherits'.
# The resulting binding is the union of the inherited bindings and this binding
# (internally, it's a recursive dictionary merge).
#
# If a field appears both in this binding and in a binding it inherits, then
# the value in this binding takes precedence. This can be used to change a
# 'category: optional' from an inherited binding to a 'category: required' (see
# the 'properties' description below).
inherits:
- !include other.yaml # or [other1.yaml, other2.yaml]
# Files with other bindings that also apply to the node. If an attribute is set
# both in an included file and in the file that includes it, then the value
# from the including file (the file with the !include) is used.
!include other.yaml # or [other1.yaml, other2.yaml]

< parent | child >:
# parent/child is used to document implicit relation between nodes.
# This information is required to generate parent related bits in child
# attributes.
# In case parent has 'bus', slave inherits some information from master.
# parent and child should share same bus-type value.
bus: <bus-type>
# If the node describes a bus, then the bus type should be given, like below
parent:
bus: <string describing bus type, e.g. "i2c">

sub-node:
# Used for cases in which a dts node has children, and the children dont
# require/specify a 'compatible' property. The sub-node is effective the
# binding for the child.
# If the node appears on a bus, then the bus type should be given, like below.
#
# Here's an example for a pwm-leds binding in which the child nodes
# would be required to have 'pwms' properties.
# When looking for a binding for a node, the code checks if the binding for the
# parent node contains 'parent: bus: <bus type>'. If it does, then only
# bindings with a matching 'child: bus: <bus type>' are considered. This allows
# the same type of device to have different bindings depending on what bus it
# appears on.
child:
bus: <string describing bus type, e.g. "i2c">

# 'sub-node' is used to simplify cases where a node has children that can all
# use the same binding. The contents of 'sub-node' becomes the binding for each
# child node.
#
# The example below is for a binding for pwm-leds where the child nodes are
# required to have a 'pwms' property.
sub-node:
properties:
pwms:
type: compound
category: required

properties:

# 'properties' describes properties on the node, e.g.
#
# reg = <1 2>;
# current-speed = <115200>;
# label = "foo";
#
# This is used to check that required properties appear, and to
# control the format of output generated for them. Except for some
# special-cased properties like 'reg', only properties listed here will
# generate output.
#
# A typical property entry looks like this:
#
# <name of the property in the device tree - regexes are supported>:
# <property name>:
# category: <required | optional>
# type: <string | int | boolean | array | uint8-array | string-array | compound>
# description: <description of property>
# description: <description of the property>
#
# Note that uint8-array is the name for what devicetree standard calls
# bytestring: its value is hexadecimal text with whitespace ignored,
# enclosed in square brackets.
# 'uint8-array' is our name for what the device tree specification calls
# 'bytestring'. Properties of type 'uint8-array' should be set like this:
#
# The 'type' attribute is currently ignored.

# At a minimum, an entry for the 'compatible' property is required, for
# matching nodes
compatible: <list of string compatible matches>
category: required
type: string
description: compatible of node
# foo = [89 AB CD];
#
# Each value is a byte in hex.
properties:
# An entry for 'compatible' must appear, as it's used to map nodes to
# bindings
compatible:
constraint: "foo-company,bar-device"

# 'reg' describes mmio registers
reg:
category: required
type: array
description: mmio register space
# Describes a property like 'current-speed = <115200>;'. We pretend that
# it's obligatory for the example node and set 'category: required'.
current-speed:
type: int
category: required
description: Initial baud rate for bar-device

# 'interrupts' specifies the interrupts that the driver may use
interrupts:
category: required
type: array
description: required interrupts
# Describes an optional property like 'keys = "foo", "bar";'
keys:
type: string-array
category: optional
description: Keys for bar-device

# If the binding describes an interrupt controller, GPIO controller, pinmux
# device, or any other device referenced via a phandle plus a specifier (some
# additional data besides the phandle), then the cells in the specifier must be
# listed in '#cells', like below.

#
# If the specifier is empty (e.g. '#clock-cells = <0>'), then '#cells' can
# either be omitted (recommended) or set to an empty array. Note that an empty
# array is specified as '"#cells": []' in YAML.
#
# For example, say that some node has 'foo-gpios = <&gpio1 1 2>'. The <1 2>
# part of the property value is the specifier, with two cells. The node pointed
# at by &gpio1 is expected to have '#gpio-cells = <2>', and its binding should
# have two elements in '#cells', corresponding to the 1 and 2 values above.
"#cells":
- cell0 # name of first cell
- cell1 # name of second cell
- cell2 # name of third cell
- and so on and so forth

# If the specifier is empty (e.g. '#clock-cells = <0>'), then '#cells' can
# either be omitted (recommended) or set to an empty array. Note that an empty
# array is specified as '"#cells": []' in YAML.
#
# For example, say that some device tree node has 'foo-gpios = <&gpio1 1 2>'.
# The <1 2> part of the property value is the specifier, with two cells in this
# example. The node pointed at by &gpio1 is expected to have
# '#gpio-cells = <2>', and its binding should have two elements in '#cells',
# corresponding to the 1 and 2 values above.

0 comments on commit 850e362

Please sign in to comment.