Skip to content
Browse files

scripts: edtlib: Reduce code duplication in phandle/value list parsing

Most of the logic for initializing 'clocks' and 'pwms' is the same and
can be shared. Add an EDT._simple_phandle_val_list() helper function for
initializing 'clocks', 'pwms', and any other properties on the form

    <foo>s = <phandle value phandle value ...>

where the nodes pointed at by the phandles are expected to have a
'#<foo>-cells' property.

This should make it easier to add similar properties.

There's still some code duplication in the classes (PWM, Clock, etc.),
but also some differences, so I'm wondering if requiring a class for
each might be okay. Maybe some more class-related stuff could be
factored out later.

Piggyback some related cleanup:

 - Have _phandle_val_list() take the name of the #foo-cells property
   instead of a function for fetching the value. The pattern is always
   the same.

 - Have _add_names() just take the "foo" part of "foo-names". Same
   pattern everywhere.

 - Clean up some redundant comments for stuff that's already documented
   in docstrings

 - Fix error messages printed by _named_cells() ("GPIOS controller" ->
   "GPIO controller", etc.)

Signed-off-by: Ulf Magnusson <>
  • Loading branch information...
ulfalizer authored and galak committed Aug 6, 2019
1 parent 83b50f6 commit 95deec1d24ff844c94fcad4761c65224846f9129
Showing with 73 additions and 74 deletions.
  1. +73 −74 scripts/dts/
@@ -606,7 +606,7 @@ def _prop_val(self, name, prop_type, optional):
.format(name, self.binding_path, prop_type))

def _init_regs(self):
# Initializes self.regs with a list of Register objects
# Initializes self.regs

node = self._node

@@ -630,10 +630,10 @@ def _init_regs(self):


_add_names(node, "reg-names", self.regs)
_add_names(node, "reg", self.regs)

def _init_interrupts(self):
# Initializes self.interrupts with a list of Interrupt objects
# Initializes self.interrupts

node = self._node

@@ -649,7 +649,7 @@ def _init_interrupts(self):


_add_names(node, "interrupt-names", self.interrupts)
_add_names(node, "interrupt", self.interrupts)

def _init_gpios(self):
# Initializes self.gpios
@@ -664,25 +664,19 @@ def _init_gpios(self):
gpio = GPIO() = self
gpio.controller = controller
gpio.specifier = self._named_cells(controller, spec, "GPIOS")
gpio.specifier = self._named_cells(controller, spec, "GPIO") = prefix


def _init_clocks(self):
# Initializes self.clocks

node = self._node

self.clocks = []
self.clocks = self._simple_phandle_val_list("clock", Clock)

for controller_node, spec in _clocks(node):
controller = self.edt._node2dev[controller_node]

clock = Clock() = self
clock.controller = controller
clock.specifier = self._named_cells(controller, spec, "clocks")
# Initialize Clock.frequency
for clock in self.clocks:
controller = clock.controller
if "fixed-clock" in controller.compats:
if "clock-frequency" not in controller.props:
_err("{!r} is a 'fixed-clock', but either lacks a "
@@ -693,26 +687,55 @@ def _init_clocks(self):
clock.frequency = None


_add_names(node, "clock-names", self.clocks)

def _init_pwms(self):
# Initializes self.pwms

self.pwms = []
self.pwms = self._simple_phandle_val_list("pwm", PWM)

for controller_node, spec in _pwms(self._node):
controller = self.edt._node2dev[controller_node]
def _simple_phandle_val_list(self, name, cls):
# Helper for parsing properties like
# <name>s = <phandle value phandle value ...>
# (e.g., gpios = <&foo 1 2 &bar 3 4>)
# , where each phandle points to a node that has a
# #<name>-cells = <size>
# property that gives the number of cells in the value after the
# phandle. Also parses any
# <name>-names = "...", "...", ...
# Arguments:
# name:
# The <name>, e.g. "clock" or "pwm". Note: no -s in the argument.
# cls:
# A class object. Instances of this class will be created and the
# 'dev', 'controller', 'specifier', and 'name' fields initialized.
# See the documentation for e.g. the PWM class.
# Returns a list of 'cls' instances.

pwm = PWM() = self
pwm.controller = controller
pwm.specifier = self._named_cells(controller, spec, "pwms")
prop = self._node.props.get(name + "s")
if not prop:
return []

res = []

_add_names(self._node, "pwm-names", self.pwms)
for controller_node, spec in _phandle_val_list(prop, name):
obj = cls() = self
obj.controller = self.edt._node2dev[controller_node]
obj.specifier = self._named_cells(obj.controller, spec, name)


_add_names(self._node, name, res)

return res

def _named_cells(self, controller, spec, controller_s):
# _init_{interrupts,gpios}() helper. Returns a dictionary that maps
@@ -1234,16 +1257,18 @@ def _add_names(node, names_ident, objs):
# Device tree node
# names-ident:
# name of property holding names, e.g. "reg-names"
# The <foo> part of <foo>-names, e.g. "reg" for "reg-names"
# objs:
# list of objects whose .name field should be set

if names_ident in node.props:
names = node.props[names_ident].to_strings()
full_names_ident = names_ident + "-names"

if full_names_ident in node.props:
names = node.props[full_names_ident].to_strings()
if len(names) != len(objs):
_err("{} property in {} has {} strings, expected {} strings"
.format(names_ident,, len(names), len(objs)))
.format(full_names_ident,, len(names), len(objs)))

for obj, name in zip(objs, names): = name
@@ -1276,7 +1301,7 @@ def _interrupts(node):
if "interrupts-extended" in node.props:
prop = node.props["interrupts-extended"]
return [_map_interrupt(node, iparent, spec)
for iparent, spec in _phandle_val_list(prop, _interrupt_cells)]
for iparent, spec in _phandle_val_list(prop, "interrupt")]

if "interrupts" in node.props:
# Treat 'interrupts' as a special case of 'interrupts-extended', with
@@ -1324,26 +1349,6 @@ def spec_len_fn(node):
return (parent, raw_spec[4*own_address_cells(parent):])

def _clocks(node):
# Returns a list of (<controller>, <specifier>) tuples for any 'clocks'
# property on 'node', or an empty list if 'node' has no 'clocks'.
# <controller> is a dtlib.Node.

if "clocks" not in node.props:
return []
return _phandle_val_list(node.props["clocks"], _clock_cells)

def _pwms(node):
# Returns a list of (<controller>, <specifier>) tuples for any 'pwms'
# property on 'node', or an empty list if 'node' has no 'pwms'.
# <controller> is a dtlib.Node.

if "pwms" not in node.props:
return []
return _phandle_val_list(node.props["pwms"], _pwm_cells)

def _gpios(node):
# Returns a dictionary that maps '<prefix>-gpios' prefixes to lists of
# (<controller>, <specifier>) tuples (possibly after mapping through an
@@ -1363,7 +1368,7 @@ def _gpios(node):

res[prefix] = [
_map_gpio(prop.node, controller, spec)
for controller, spec in _phandle_val_list(prop, _gpio_cells)
for controller, spec in _phandle_val_list(prop, "gpio")

return res
@@ -1540,19 +1545,22 @@ def _not(b):
return bytes(~x & 0xFF for x in b)

def _phandle_val_list(prop, n_cells_fn):
# Parses a '<phandle> <value> <phandle> <value> ...' value
def _phandle_val_list(prop, n_cells_name):
# Parses a '<phandle> <value> <phandle> <value> ...' value. The number of
# cells that make up each <value> is derived from the node pointed at by
# the preceding <phandle>.
# prop:
# dtlib.Property with value to parse
# cells_fn:
# A function that returns the number of cells for a <value>. Takes the
# node pointed at by the preceding <phandle> as its argument.
# n_cells_name:
# The <name> part of the #<name>-cells property to look for on the nodes
# the phandles point to, e.g. "gpio" for #gpio-cells.
# Returns a list of (<node>, <value>) tuples, where <node> is the node
# pointed at by <phandle>. 'cells_fn' is a function called on <node> to get
# the number of cells for <value>.
# pointed at by <phandle>.

full_n_cells_name = "#{}-cells".format(n_cells_name)

res = []

@@ -1568,7 +1576,10 @@ def _phandle_val_list(prop, n_cells_fn):
if not node:
_err("bad phandle in " + repr(prop))

n_cells = n_cells_fn(node)
if full_n_cells_name not in node.props:
_err("{!r} lacks {}".format(node, full_n_cells_name))

n_cells = node.props[full_n_cells_name].to_num()
if len(raw) < 4*n_cells:
_err("missing data after phandle in " + repr(prop))

@@ -1611,18 +1622,6 @@ def _gpio_cells(node):
return node.props["#gpio-cells"].to_num()

def _pwm_cells(node):
if "#pwm-cells" not in node.props:
_err("{!r} lacks #pwm-cells".format(node))
return node.props["#pwm-cells"].to_num()

def _clock_cells(node):
if "#clock-cells" not in node.props:
_err("{!r} lacks #clock-cells".format(node))
return node.props["#clock-cells"].to_num()

def _slice(node, prop_name, size):
# Splits node.props[prop_name].value into 'size'-sized chunks, returning a
# list of chunks. Raises EDTError if the length of the property is not

0 comments on commit 95deec1

Please sign in to comment.
You can’t perform that action at this time.