Commit b97ed9e4 authored by Ulf Magnusson's avatar Ulf Magnusson Committed by Kumar Gala
Browse files

scripts: dts: Generalize handling of phandle-array types



Generating generic information for 'type: phandle-array' properties in
edtlib was difficult due to defining phandle-array as just a list of
phandles and numbers. To make sense of a phandle-array property like
'pwms', you have to know that #pwm-cells is expected to appear on
each referenced controller, and that the binding for the controller has
a #cells.

Because of this, handling of various 'type: phandle-array' properties
was previously hardcoded in edtlib and exposed through properties like
Node.pwms, instead of through the generic Node.props (though with a lot
of shared code).

In practice, it turns out that all 'type: phandle-array' properties in
Zephyr work exactly the same way: They all have names that end in -s,
the 's' is removed to derive the name of related properties, and they
all look up #cells in the binding for the controller, which gives names
to the data values.

Strengthen the definition of 'type: phandle-array' to mean a property
that works exactly like the existing phandle-array properties (which
also means requiring that the name ends in -s). This removes a ton of
hardcoding from edtlib and allows new 'type: phandle-array' properties
to be added without making any code changes.

If we ever need a property type that's a list of phandles and numbers
but that doesn't follow this scheme, then we could add a separate type
for it. We should check if the standard scheme is fine first though.

The only property type for which no information is generated is now
'compound'.

There's some inconsistency in how we generate identifiers for clocks
compared to other 'type: phandle-array' properties, so keep
special-casing them for now in gen_defines.py (see the comment in
write_clocks()).

This change also enabled a bunch of other simplifications, like reusing
the ControllerAndData class for interrupts.

Piggyback generalization of *-map properties so that they work for any
phandle-array properties. It's now possible to have things like
'io-channel-map', if you need to.

Signed-off-by: default avatarUlf Magnusson <Ulf.Magnusson@nordicsemi.no>
parent d82f76a0
Loading
Loading
Loading
Loading
+51 −22
Original line number Diff line number Diff line
@@ -86,35 +86,64 @@ parent-bus: <string describing bus type, e.g. "i2c">
#     const: <string | int>
#     default: <default>
#
# 'type: boolean' is for properties used as flags that don't take a value, e.g.
# 'hw-flow-control;'. The macro generated for the property gets set to 1 if the
# property exists on the node, and to 0 otherwise. When combined with
# 'required: true', this type just forces the flag to appear on the node,
# though the output will always be the same in that case (1).
# These types are available:
#
# 'type: uint8-array' is for what the device tree specification calls
#   - 'type: boolean' is for properties used as flags that don't take a value,
#     e.g. 'hw-flow-control;'. The macro generated for the property gets set to
#     1 if the property exists on the node, and to 0 otherwise. When combined
#     with 'required: true', this type just forces the flag to appear on the
#     node, though the output will always be the same in that case (1).
#
#     Warning: Since a macro is always generated, don't use #ifdef in tests.
#     Do this instead:
#
#       #if DT_SOME_BOOLEAN_PROP == 1
#
#   - 'type: uint8-array' is for what the device tree specification calls
#     'bytestring'. Properties of type 'uint8-array' should be set like this:
#
#       foo = [89 AB CD];
#
#     Each value is a byte in hex.
#
# 'type: phandle' is for properties that are assigned a single phandle, like
# this:
#   - 'type: phandle' is for properties that are assigned a single phandle,
#     like this:
#
#       foo = <&label>;
#
# 'type: phandles' is for properties that are assigned a list of (just)
#   - 'type: phandles' is for properties that are assigned a list of (just)
#     phandles, like this:
#
#       foo = <&label1 &label2 ...>;
#
# 'type: phandle-array' is for properties that are assigned a list of phandles
# and (possibly) 32-bit numbers, like this:
#   - 'type: phandle-array' is for properties that take a list of phandles and
#     (possibly) 32-bit numbers, like this:
#
#       pwms = <&ctrl-1 1 2 &ctrl-2 3 4>;
#
#     This type requires that the property works in the standard way that
#     devicetree properties like pwms, clocks, *-gpios, and io-channels work.
#     Taking 'pwms' as an example, the final -s is stripped from the property
#     name, and #pwm-cells is looked up in the node for the controller
#     (&ctrl-1/&ctrl-2) to determine the number of data values after the
#     phandle. The binding for each controller must also have a #cells key,
#     giving names to data values. See below for an explanation of #cells.
#
#     A *-names (e.g. pwm-names) property can appear on the node as well,
#     giving a name to each entry (the 'pwms' example above has two entries,
#     <&ctrl-1 1 2> and <&ctrl-2 3 4>).
#
#     Because other property names are derived from the name of the property by
#     removing the final -s, the property name must end in -s. An error is
#     raised if it doesn't.
#
#     *-gpios properties are special-cased so that e.g. foo-gpios resolves to
#     #gpio-cells rather than #foo-gpio-cells.
#
#   foo = <&label1 1 2 &label2 3 4 ...>;
#     All phandle-array properties support mapping through *-map properties,
#     e.g. gpio-map. See the devicetree spec.
#
# 'type: compound' is a catch-all for more complex types, e.g.
#   - 'type: compound' is a catch-all for more complex types, e.g.
#
#       foo = <&label1>, [01 02];
#
@@ -256,8 +285,8 @@ child-binding:
                required: true

# If the binding describes an interrupt controller, GPIO controller, pinmux
# device, or any other node referenced by other nodes, then #cells should be
# given.
# device, or any other node referenced by other nodes via 'phandle-array'
# properties, then #cells should be given.
#
# To understand the purpose of #cells, assume that some node has
#
+4 −1
Original line number Diff line number Diff line
@@ -32,8 +32,11 @@ properties:
        required: false
        description: interrupts for device

    # Does not follow the 'type: phandle-array' scheme, but gets type-checked
    # by the code. Declare it here just so that other bindings can make it
    # 'required: true' easily if they want to.
    interrupts-extended:
        type: phandle-array
        type: compound
        required: false
        description: extended interrupt specifier for device

+131 −335

File changed.

Preview size limit exceeded, changes collapsed.

+70 −47
Original line number Diff line number Diff line
@@ -61,13 +61,9 @@ def main():

            write_regs(node)
            write_irqs(node)
            for gpios in node.gpios.values():
                write_phandle_val_list(node, gpios, "GPIO")
            write_phandle_val_list(node, node.pwms, "PWM")
            write_phandle_val_list(node, node.iochannels, "IO_CHANNEL")
            write_props(node)
            write_clocks(node)
            write_spi_dev(node)
            write_props(node)
            write_bus(node)
            write_existence_flags(node)

@@ -143,7 +139,13 @@ def write_props(node):
        if prop.name[0] == "#" or prop.name.endswith("-map"):
            continue

        # Skip phandles
        # See write_clocks()
        if prop.name == "clocks":
            continue

        # edtlib provides these as well (Property.val becomes an edtlib.Node
        # and a list of edtlib.Nodes, respectively). Nothing is generated for
        # them currently though.
        if prop.type in {"phandle", "phandles"}:
            continue

@@ -174,6 +176,8 @@ def write_props(node):
        elif prop.type == "uint8-array":
            out_dev(node, ident,
                    "{ " + ", ".join("0x{:02x}".format(b) for b in prop.val) + " }")
        elif prop.type == "phandle-array":
            write_phandle_val_list(prop)

        # Generate DT_..._ENUM if there's an 'enum:' key in the binding
        if prop.enum_index is not None:
@@ -452,26 +456,24 @@ def write_spi_dev(node):

    cs_gpio = edtlib.spi_dev_cs_gpio(node)
    if cs_gpio is not None:
        write_phandle_val_list_entry(node, cs_gpio, None, "GPIO")
        write_phandle_val_list_entry(node, cs_gpio, None, "CS_GPIOS")


def write_phandle_val_list(node, entries, ident):
def write_phandle_val_list(prop):
    # Writes output for a phandle/value list, e.g.
    #
    #    pwms = <&pwm-ctrl-1 10 20
    #            &pwm-ctrl-2 30 40>;
    #
    # node:
    #   Device used to generate device prefixes (see 'ident' below)
    # prop:
    #   phandle/value Property instance.
    #
    # entries:
    #   List of entries (two for 'pwms' above). This might be a list of
    #   edtlib.PWM instances, for example.  If only one entry is given it
    #   does not have a suffix '_0', and the '_COUNT' and group initializer
    #   are not emitted.
    #   If only one entry appears in 'prop' (the example above has two), the
    #   generated identifier won't get a '_0' suffix, and the '_COUNT' and
    #   group initializer are skipped too.
    #
    # ident:
    #   Base identifier. For example, "PWM" generates output like this:
    # The base identifier is derived from the property name. For example, 'pwms = ...'
    # generates output like this:
    #
    #   #define <device prefix>_PWMS_CONTROLLER_0 "PWM_0"  (name taken from 'label = ...')
    #   #define <device prefix>_PWMS_CHANNEL_0 123         (name taken from #cells in binding)
@@ -482,29 +484,33 @@ def write_phandle_val_list(node, entries, ident):
    #   #define <device prefix>_PWMS_COUNT 2
    #   #define <device prefix>_PWMS {<device prefix>_PWMS_0, <device prefix>_PWMS_1}
    #   ...
    #
    #   Note: Do not add an "S" to 'ident'. It's added automatically, which
    #   forces consistency.

    # pwms -> PWMS
    # foo-gpios -> FOO_GPIOS
    ident = str2ident(prop.name)

    initializer_vals = []
    for i, entry in enumerate(entries):
    for i, entry in enumerate(prop.val):
        initializer_vals.append(write_phandle_val_list_entry(
            node, entry, i if len(entries) > 1 else None, ident))
    if len(entries) > 1:
        out_dev(node, ident + "S_COUNT", len(initializer_vals))
        out_dev(node, ident + "S", "{" + ", ".join(initializer_vals) + "}")
            prop.node, entry, i if len(prop.val) > 1 else None, ident))

    if len(prop.val) > 1:
        out_dev(prop.node, ident + "_COUNT", len(initializer_vals))
        out_dev(prop.node, ident, "{" + ", ".join(initializer_vals) + "}")


def write_phandle_val_list_entry(node, entry, i, ident):
    # write_phandle_val_list() helper. We could get rid of it if it wasn't for
    # write_spi_dev(). Adds 'i' as an index to identifiers unless it's None.
    #
    # 'entry' is an edtlib.ControllerAndData instance.
    #
    # Returns the identifier for the macro that provides the
    # initializer for the entire entry.

    initializer_vals = []
    if entry.controller.label is not None:
        ctrl_ident = ident + "S_CONTROLLER"  # e.g. PWMS_CONTROLLER
        ctrl_ident = ident + "_CONTROLLER"  # e.g. PWMS_CONTROLLER
        if entry.name:
            ctrl_ident = str2ident(entry.name) + "_" + ctrl_ident
        # Ugly backwards compatibility hack. Only add the index if there's
@@ -515,7 +521,7 @@ def write_phandle_val_list_entry(node, entry, i, ident):
        out_dev_s(node, ctrl_ident, entry.controller.label)

    for cell, val in entry.data.items():
        cell_ident = ident + "S_" + str2ident(cell)  # e.g. PWMS_CHANNEL
        cell_ident = ident + "_" + str2ident(cell)  # e.g. PWMS_CHANNEL
        if entry.name:
            # From e.g. 'pwm-names = ...'
            cell_ident = str2ident(entry.name) + "_" + cell_ident
@@ -526,7 +532,7 @@ def write_phandle_val_list_entry(node, entry, i, ident):

    initializer_vals += entry.data.values()

    initializer_ident = ident + "S"
    initializer_ident = ident
    if entry.name:
        initializer_ident += "_" + str2ident(entry.name)
    if i is not None:
@@ -536,25 +542,42 @@ def write_phandle_val_list_entry(node, entry, i, ident):


def write_clocks(node):
    # Writes clock controller and clock data for the clock in the node's
    # 'clock' property
    # Writes clock information.
    #
    # Most of this ought to be handled in write_props(), but the identifiers
    # that get generated for 'clocks' are inconsistent with the with other
    # 'phandle-array' properties.
    #
    # See https://github.com/zephyrproject-rtos/zephyr/pull/19327#issuecomment-534081845.

    for clock_i, clock in enumerate(node.clocks):
        if clock.controller.label is not None:
            out_dev_s(node, "CLOCK_CONTROLLER", clock.controller.label)
    if "clocks" not in node.props:
        return

        if clock.frequency is not None:
            out_dev(node, "CLOCKS_CLOCK_FREQUENCY", clock.frequency)
    for clock_i, clock in enumerate(node.props["clocks"].val):
        controller = clock.controller

        if controller.label is not None:
            out_dev_s(node, "CLOCK_CONTROLLER", controller.label)

        for spec, val in clock.data.items():
        for name, val in clock.data.items():
            if clock_i == 0:
                clk_name_alias = "CLOCK_" + str2ident(spec)
                clk_name_alias = "CLOCK_" + str2ident(name)
            else:
                clk_name_alias = None

            out_dev(node, "CLOCK_{}_{}".format(str2ident(spec), clock_i), val,
            out_dev(node, "CLOCK_{}_{}".format(str2ident(name), clock_i), val,
                    name_alias=clk_name_alias)

        if "fixed-clock" not in controller.compats:
            continue

        if "clock-frequency" not in controller.props:
            err("{!r} is a 'fixed-clock' but lacks a 'clock-frequency' "
                "property".format(controller))

        out_dev(node, "CLOCKS_CLOCK_FREQUENCY",
                controller.props["clock-frequency"].val)


def str2ident(s):
    # Converts 's' to a form suitable for (part of) an identifier
+0 −10
Original line number Diff line number Diff line
# SPDX-License-Identifier: BSD-3-Clause

title: Clock source with two cells
description: Clock source with two cells

compatible: "clock-two-cell"

"#cells":
  - one
  - two
Loading