Skip to content

dts: Add device type handing support #17976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 68 additions & 14 deletions scripts/dts/edtlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def _init_compat2binding(self, bindings_dirs):
# are loaded.

dt_compats = _dt_compats(self._dt)
dt_device_types = _dt_device_types(self._dt)
self._binding_paths = _binding_paths(bindings_dirs)

# Add '!include foo.yaml' handling.
Expand All @@ -172,12 +173,17 @@ def _init_compat2binding(self, bindings_dirs):
yaml.Loader.add_constructor("!include", self._binding_include)

self._compat2binding = {}
self._device_type2binding = {}
for binding_path in self._binding_paths:
compat = _binding_compat(binding_path)
if compat in dt_compats:
constraint = _binding_constraint(binding_path)
if constraint in dt_compats:
binding = _load_binding(binding_path)
self._compat2binding[compat, _binding_bus(binding)] = \
self._compat2binding[constraint, _binding_bus(binding)] = \
(binding, binding_path)
if constraint in dt_device_types:
binding = _load_binding(binding_path)
self._device_type2binding[constraint] = (binding, binding_path)


def _binding_include(self, loader, node):
# Implements !include. Returns a list with the YAML structures for the
Expand Down Expand Up @@ -287,9 +293,15 @@ class Device:
Only enabled devices (status != "disabled") are counted. 'instance_no' is
meaningless for disabled devices.

matching_compat:
The 'compatible' string for the binding that matched the device, or
None if the device has no binding
matching_constraint:
The constraint string for the binding that matched the device (typically
the 'compatible' string that matched), or None if the device has no
binding

matching_property:
A string with the property name that the binding matched on. This would
be something like 'compatible' or 'device_type'. None if the device has
no binding

description:
The description string from the binding file for the device, or None if
Expand All @@ -304,6 +316,10 @@ class Device:
A list of 'compatible' strings for the device, in the same order that
they're listed in the .dts file

device_type:
The 'device_type' string for the device if it has a 'device_type' property
or None if the property doesn't exist

regs:
A list of Register objects for the device's registers

Expand Down Expand Up @@ -423,7 +439,7 @@ def flash_controller(self):
.format(self))

controller = self.parent.parent
if controller.matching_compat == "soc-nv-flash":
if controller.matching_constraint == "soc-nv-flash":
return controller.parent
return controller

Expand All @@ -442,13 +458,18 @@ def __init__(self, edt, node):
self.edt = edt
self._node = node

if "device_type" in self._node.props:
self.device_type = self._node.props["device_type"].to_string()
else:
self.device_type = None

self._init_binding()
self._init_props()
self._init_regs()
self._set_instance_no()

def _init_binding(self):
# Initializes Device.matching_compat, Device._binding, and
# Initializes Device.matching_constraint, Device._binding, and
# Device.binding_path.
#
# Device._binding holds the data from the device's binding file, in the
Expand All @@ -458,6 +479,7 @@ def _init_binding(self):
# This relies on the parent of the Device having already been
# initialized, which is guaranteed by going through the nodes in
# node_iter() order.
self.compats = []

if "compatible" in self._node.props:
self.compats = self._node.props["compatible"].to_strings()
Expand All @@ -466,7 +488,8 @@ def _init_binding(self):
for compat in self.compats:
if (compat, bus) in self.edt._compat2binding:
# Binding found
self.matching_compat = compat
self.matching_constraint = compat
self.matching_property = "compatible"
self._binding, self.binding_path = \
self.edt._compat2binding[compat, bus]

Expand All @@ -475,6 +498,19 @@ def _init_binding(self):
self.description = self.description.rstrip()

return
elif self.device_type:
if self.device_type in self.edt._device_type2binding:
# Binding found
self.matching_constraint = self.device_type
self.matching_property = "device_type"
self._binding, self.binding_path = \
self.edt._device_type2binding[self.device_type]

self.description = self._binding.get("description")
if self.description:
self.description = self.description.rstrip()

return
else:
# No 'compatible' property. See if the parent has a 'sub-node:' key
# that gives the binding.
Expand All @@ -492,12 +528,13 @@ def _init_binding(self):
if self.description:
self.description = self.description.rstrip()

self.matching_compat = self.parent.matching_compat
self.matching_constraint = self.parent.matching_constraint
self.matching_property = self.parent.matching_property
return

# No binding found
self.matching_compat = self._binding = self.binding_path = \
self.description = None
self.matching_constraint = self._binding = self.binding_path = \
self.description = self.matching_property = None

def _bus_from_parent_binding(self):
# _init_binding() helper. Returns the bus specified by
Expand Down Expand Up @@ -777,6 +814,13 @@ def _set_instance_no(self):
if compat in other_dev.compats and other_dev.enabled:
self.instance_no[compat] += 1

device_type = self.device_type
if device_type:
self.instance_no[device_type] = 0
for other_dev in self.edt.devices:
if device_type == other_dev.device_type and other_dev.enabled:
self.instance_no[device_type] += 1


class Register:
"""
Expand Down Expand Up @@ -1032,6 +1076,16 @@ def _dt_compats(dt):
for compat in node.props["compatible"].to_strings()}


def _dt_device_types(dt):
# Returns a set() with all 'device_type' strings in the device tree
# represented by dt (a dtlib.DT instance)

return {device_type
for node in dt.node_iter()
if "device_type" in node.props
for device_type in node.props["device_type"].to_strings()}


def _binding_paths(bindings_dirs):
# Returns a list with the paths to all bindings (.yaml files) in
# 'bindings_dirs'
Expand All @@ -1047,8 +1101,8 @@ def _binding_paths(bindings_dirs):
return binding_paths


def _binding_compat(binding_path):
# Returns the compatible string specified in the binding at 'binding_path'.
def _binding_constraint(binding_path):
# Returns the constraint string specified in the binding at 'binding_path'.
# Uses a regex to avoid having to parse the bindings, which is slow when
# done for all bindings.

Expand Down
34 changes: 21 additions & 13 deletions scripts/dts/gen_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@ def main():
active_compats = set()

for dev in edt.devices:
if dev.enabled and dev.matching_compat:
if dev.enabled and dev.matching_constraint:
# Skip 'fixed-partitions' devices since they are handled by
# write_flash() and would generate extra spurious #defines
if dev.matching_compat == "fixed-partitions":
if dev.matching_constraint == "fixed-partitions":
continue

out_comment("Device tree node: " + dev.path)
out_comment("Binding (compatible = {}): {}".format(
dev.matching_compat, dev.binding_path),
out_comment("Binding ({} = {}): {}".format(
dev.matching_property, dev.matching_constraint,
dev.binding_path),
blank_before=False)
out_comment("Binding description: " + dev.description,
blank_before=False)
Expand Down Expand Up @@ -154,7 +155,7 @@ def write_props(dev):
continue

# Skip properties that we handle elsewhere
if prop.name in {"reg", "interrupts", "compatible"}:
if prop.name in {"reg", "interrupts", "compatible", "device_type"}:
continue

if prop.description is not None:
Expand Down Expand Up @@ -205,8 +206,11 @@ def write_existence_flags(dev):
#
# These are flags for which devices exist.

for compat in dev.compats:
out("INST_{}_{}".format(dev.instance_no[compat], str2ident(compat)), 1)
if dev.compats:
for compat in dev.compats:
out("INST_{}_{}".format(dev.instance_no[compat], str2ident(compat)), 1)
if dev.device_type:
out("INST_{}_{}".format(dev.instance_no[dev.device_type], str2ident(dev.device_type)), 1)


def reg_addr_ident(reg):
Expand Down Expand Up @@ -248,9 +252,9 @@ def dev_ident(dev):

if dev.bus:
ident += "{}_{:X}_".format(
str2ident(dev.parent.matching_compat), dev.parent.unit_addr)
str2ident(dev.parent.matching_constraint), dev.parent.unit_addr)

ident += "{}_".format(str2ident(dev.matching_compat))
ident += "{}_".format(str2ident(dev.matching_constraint))

if dev.unit_addr is not None:
ident += "{:X}".format(dev.unit_addr)
Expand All @@ -275,10 +279,10 @@ def dev_path_aliases(dev):
# registered for the device, in the /aliases node. Used when building e.g.
# macro names.

if dev.matching_compat is None:
if dev.matching_constraint is None:
return []

compat_s = str2ident(dev.matching_compat)
compat_s = str2ident(dev.matching_constraint)

aliases = []
for alias in dev.aliases:
Expand All @@ -297,8 +301,12 @@ def dev_instance_aliases(dev):
# This is a list since a device can have multiple 'compatible' strings,
# each with their own instance number.

return ["INST_{}_{}".format(dev.instance_no[compat], str2ident(compat))
for compat in dev.compats]
if dev.compats:
return ["INST_{}_{}".format(dev.instance_no[compat], str2ident(compat))
for compat in dev.compats]
elif dev.device_type:
return ["INST_{}_{}".format(dev.instance_no[dev.device_type], str2ident(dev.device_type))]
return []


def write_addr_size(edt, prop_name, prefix):
Expand Down