dts: Add new DTS/binding parser
Add a new DTS/binding parser to scripts/dts/ for generating generated_dts_board.conf and generated_dts_board_unfixed.h. The old code is kept to generate some deprecated defines, using the --deprecated-only flag. It will be removed later. The new parser is implemented in three files in scripts/dts/: dtlib.py: A low-level .dts parsing library. This is similar to devicetree.py in the old code, but is a general robust DTS parser that doesn't rely on preprocessing. edtlib.py (e for extended): A library built on top of dtlib.py that brings together data from DTS files and bindings and creates Device instances with all the data for a device. gen_defines.py: A script that uses edtlib.py to generate generated_dts_board.conf and generated_dts_board_unfixed.h. Corresponds to extract_dts_includes.py and the files in extract/ in the old code. testdtlib.py: Test suite for dtlib.py. Can be run directly as a script. testedtlib.py (uses test.dts and test-bindings/): Test suite for edtlib.py. Can be run directly as a script. The test suites will be run automatically in CI. The new code turns some things that were warnings (or not checked) in the old code into errors, like missing properties that are specified with 'category: required' in the binding for the node. The code includes lots of documentation and tries to give helpful error messages instead of Python errors. Co-authored-by: Kumar Gala <kumar.gala@linaro.org> Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
parent
a72e451624
commit
62d5741476
|
@ -311,6 +311,7 @@
|
|||
/scripts/elf_helper.py @andrewboie
|
||||
/scripts/sanity_chk/expr_parser.py @nashif
|
||||
/scripts/gen_app_partitions.py @andrewboie
|
||||
/scripts/dts/ @ulfalizer @galak
|
||||
/arch/x86/gen_gdt.py @andrewboie
|
||||
/arch/x86/gen_idt.py @andrewboie
|
||||
/scripts/gen_kobject_list.py @andrewboie
|
||||
|
|
|
@ -156,16 +156,39 @@ if(SUPPORTS_DTS)
|
|||
message(FATAL_ERROR "command failed with return code: ${ret}")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Run gen_defines.py to create a .conf file and a header file
|
||||
#
|
||||
|
||||
set(CMD_NEW_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_defines.py
|
||||
--dts ${BOARD}.dts.pre.tmp
|
||||
--bindings-dir ${DTS_ROOT_BINDINGS}
|
||||
--conf-out ${GENERATED_DTS_BOARD_CONF}
|
||||
--header-out ${GENERATED_DTS_BOARD_UNFIXED_H}
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMD_NEW_EXTRACT}
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
RESULT_VARIABLE ret
|
||||
)
|
||||
if(NOT "${ret}" STREQUAL "0")
|
||||
message(FATAL_ERROR "new extractor failed with return code: ${ret}")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Run extract_dts_includes.py (the older DT/binding parser) to generate some
|
||||
# legacy identifiers (via --deprecated-only). This will go away later.
|
||||
#
|
||||
|
||||
set(CMD_EXTRACT_DTS_INCLUDES ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/extract_dts_includes.py
|
||||
--deprecated-only
|
||||
--dts ${BOARD}.dts_compiled
|
||||
--yaml ${DTS_ROOT_BINDINGS}
|
||||
--keyvalue ${GENERATED_DTS_BOARD_CONF}
|
||||
--include ${GENERATED_DTS_BOARD_UNFIXED_H}
|
||||
--include ${GENERATED_DTS_BOARD_UNFIXED_H}.deprecated
|
||||
--old-alias-names
|
||||
)
|
||||
|
||||
# Run extract_dts_includes.py to create a .conf and a header file that can be
|
||||
# included into the CMake namespace
|
||||
execute_process(
|
||||
COMMAND ${CMD_EXTRACT_DTS_INCLUDES}
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
|
@ -179,4 +202,5 @@ if(SUPPORTS_DTS)
|
|||
|
||||
else()
|
||||
file(WRITE ${GENERATED_DTS_BOARD_UNFIXED_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */")
|
||||
file(WRITE ${GENERATED_DTS_BOARD_UNFIXED_H}.deprecated "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */")
|
||||
endif(SUPPORTS_DTS)
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <generated_dts_board_unfixed.h>
|
||||
|
||||
#include <generated_dts_board_unfixed.h.deprecated>
|
||||
|
||||
/* The following definitions fixup the generated include */
|
||||
|
||||
#include <generated_dts_board_fixups.h>
|
||||
|
|
1689
scripts/dts/dtlib.py
Normal file
1689
scripts/dts/dtlib.py
Normal file
File diff suppressed because it is too large
Load diff
1639
scripts/dts/edtlib.py
Normal file
1639
scripts/dts/edtlib.py
Normal file
File diff suppressed because it is too large
Load diff
619
scripts/dts/gen_defines.py
Executable file
619
scripts/dts/gen_defines.py
Executable file
|
@ -0,0 +1,619 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
# Copyright (c) 2019 Linaro Limited
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# This script uses edtlib to generate a header file and a .conf file (both
|
||||
# containing the same values) from a device tree (.dts) file. Information from
|
||||
# binding files in YAML format is used as well.
|
||||
#
|
||||
# Bindings are files that describe device tree nodes. Device tree nodes are
|
||||
# usually mapped to bindings via their 'compatible = "..."' property.
|
||||
#
|
||||
# See the docstring/comments at the top of edtlib.py for more information.
|
||||
#
|
||||
# Note: Do not access private (_-prefixed) identifiers from edtlib here (and
|
||||
# also note that edtlib is not meant to expose the dtlib API directly).
|
||||
# Instead, think of what API you need, and add it as a public documented API in
|
||||
# edtlib. This will keep this script simple.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
import edtlib
|
||||
|
||||
|
||||
def main():
|
||||
global conf_file
|
||||
global header_file
|
||||
|
||||
args = parse_args()
|
||||
|
||||
try:
|
||||
edt = edtlib.EDT(args.dts, args.bindings_dir)
|
||||
except edtlib.EDTError as e:
|
||||
sys.exit("device tree error: " + str(e))
|
||||
|
||||
conf_file = open(args.conf_out, "w", encoding="utf-8")
|
||||
header_file = open(args.header_out, "w", encoding="utf-8")
|
||||
|
||||
out_comment("Generated by gen_defines.py", blank_before=False)
|
||||
out_comment("DTS input file: " + args.dts, blank_before=False)
|
||||
out_comment("Directory with bindings: " + args.bindings_dir,
|
||||
blank_before=False)
|
||||
|
||||
active_compats = set()
|
||||
|
||||
for dev in edt.devices:
|
||||
if dev.enabled and dev.matching_compat:
|
||||
# Skip 'fixed-partitions' devices since they are handled by
|
||||
# write_flash() and would generate extra spurious #defines
|
||||
if dev.matching_compat == "fixed-partitions":
|
||||
continue
|
||||
|
||||
out_comment("Device tree node: " + dev.path)
|
||||
out_comment("Binding (compatible = {}): {}".format(
|
||||
dev.matching_compat, dev.binding_path),
|
||||
blank_before=False)
|
||||
out_comment("Binding description: " + dev.description,
|
||||
blank_before=False)
|
||||
|
||||
write_regs(dev)
|
||||
write_irqs(dev)
|
||||
write_gpios(dev)
|
||||
write_pwms(dev)
|
||||
write_clocks(dev)
|
||||
write_spi_dev(dev)
|
||||
write_props(dev)
|
||||
write_bus(dev)
|
||||
write_existence_flags(dev)
|
||||
|
||||
active_compats.update(dev.compats)
|
||||
|
||||
out_comment("Active compatibles (mentioned in DTS + binding found)")
|
||||
for compat in active_compats:
|
||||
#define DT_COMPAT_<COMPAT> 1
|
||||
out("COMPAT_{}".format(str2ident(compat)), 1)
|
||||
|
||||
# These are derived from /chosen
|
||||
write_addr_size(edt, "zephyr,sram", "SRAM")
|
||||
write_addr_size(edt, "zephyr,ccm", "CCM")
|
||||
write_addr_size(edt, "zephyr,dtcm", "DTCM")
|
||||
|
||||
# NOTE: These defines aren't used by the code and just used by
|
||||
# the kconfig build system, we can remove them in the future
|
||||
# if we provide a function in kconfigfunctions.py to get
|
||||
# the same info
|
||||
write_required_label("UART_CONSOLE_ON_DEV_NAME", edt.chosen_dev("zephyr,console"))
|
||||
write_required_label("UART_SHELL_ON_DEV_NAME", edt.chosen_dev("zephyr,shell-uart"))
|
||||
write_required_label("BT_UART_ON_DEV_NAME", edt.chosen_dev("zephyr,bt-uart"))
|
||||
write_required_label("UART_PIPE_ON_DEV_NAME", edt.chosen_dev("zephyr,uart-pipe"))
|
||||
write_required_label("BT_MONITOR_ON_DEV_NAME", edt.chosen_dev("zephyr,bt-mon-uart"))
|
||||
write_required_label("UART_MCUMGR_ON_DEV_NAME", edt.chosen_dev("zephyr,uart-mcumgr"))
|
||||
write_required_label("BT_C2H_UART_ON_DEV_NAME", edt.chosen_dev("zephyr,bt-c2h-uart"))
|
||||
|
||||
write_flash(edt.chosen_dev("zephyr,flash"))
|
||||
write_code_partition(edt.chosen_dev("zephyr,code-partition"))
|
||||
|
||||
flash_index = 0
|
||||
for dev in edt.devices:
|
||||
if dev.name.startswith("partition@"):
|
||||
write_flash_partition(dev, flash_index)
|
||||
flash_index += 1
|
||||
|
||||
out_comment("Number of flash partitions")
|
||||
if flash_index != 0:
|
||||
out("FLASH_AREA_NUM", flash_index)
|
||||
|
||||
print("Device tree configuration written to " + args.conf_out)
|
||||
|
||||
|
||||
def parse_args():
|
||||
# Returns parsed command-line arguments
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--dts", required=True, help="DTS file")
|
||||
parser.add_argument("--bindings-dir", required=True,
|
||||
help="directory with bindings in YAML format")
|
||||
parser.add_argument("--header-out", required=True,
|
||||
help="path to write header to")
|
||||
parser.add_argument("--conf-out", required=True,
|
||||
help="path to write configuration file to")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def write_regs(dev):
|
||||
# Writes address/size output for the registers in dev's 'reg' property
|
||||
|
||||
def reg_addr_name_alias(reg):
|
||||
return str2ident(reg.name) + "_BASE_ADDRESS" if reg.name else None
|
||||
|
||||
def reg_size_name_alias(reg):
|
||||
return str2ident(reg.name) + "_SIZE" if reg.name else None
|
||||
|
||||
for reg in dev.regs:
|
||||
out_dev(dev, reg_addr_ident(reg), hex(reg.addr),
|
||||
name_alias=reg_addr_name_alias(reg))
|
||||
|
||||
if reg.size:
|
||||
out_dev(dev, reg_size_ident(reg), reg.size,
|
||||
name_alias=reg_size_name_alias(reg))
|
||||
|
||||
|
||||
def write_props(dev):
|
||||
# Writes any properties defined in the "properties" section of the binding
|
||||
# for the device
|
||||
|
||||
for prop in dev.props.values():
|
||||
# Skip #size-cell and other property starting with #. Also skip mapping
|
||||
# properties like 'gpio-map'.
|
||||
if prop.name[0] == "#" or prop.name.endswith("-map"):
|
||||
continue
|
||||
|
||||
# Skip properties that we handle elsewhere
|
||||
if prop.name in {"reg", "interrupts", "pwms", "clocks", "compatible"} or \
|
||||
prop.name.endswith("gpios"):
|
||||
continue
|
||||
|
||||
if prop.description is not None:
|
||||
out_comment(prop.description, blank_before=False)
|
||||
|
||||
ident = str2ident(prop.name)
|
||||
|
||||
if isinstance(prop.val, bool):
|
||||
out_dev(dev, ident, 1 if prop.val else 0)
|
||||
elif isinstance(prop.val, str):
|
||||
out_dev_s(dev, ident, prop.val)
|
||||
elif isinstance(prop.val, int):
|
||||
out_dev(dev, ident, prop.val)
|
||||
elif isinstance(prop.val, list):
|
||||
for i, elm in enumerate(prop.val):
|
||||
out_fn = out_dev_s if isinstance(elm, str) else out_dev
|
||||
out_fn(dev, "{}_{}".format(ident, i), elm)
|
||||
elif isinstance(prop.val, bytes):
|
||||
out_dev(dev, ident,
|
||||
"{ " + ", ".join("0x{:02x}".format(b) for b in prop.val) + " }")
|
||||
|
||||
# Generate DT_..._ENUM if there's an 'enum:' key in the binding
|
||||
if prop.enum_index is not None:
|
||||
out_dev(dev, ident + "_ENUM", prop.enum_index)
|
||||
|
||||
|
||||
def write_bus(dev):
|
||||
# Generate bus-related #defines
|
||||
|
||||
if not dev.bus:
|
||||
return
|
||||
|
||||
if dev.parent.label is None:
|
||||
_err("missing 'label' property on {!r}".format(dev.parent))
|
||||
|
||||
# #define DT_<DEV-IDENT>_BUS_NAME <BUS-LABEL>
|
||||
out_dev_s(dev, "BUS_NAME", str2ident(dev.parent.label))
|
||||
|
||||
for compat in dev.compats:
|
||||
# #define DT_<COMPAT>_BUS_<BUS-TYPE> 1
|
||||
out("{}_BUS_{}".format(str2ident(compat), str2ident(dev.bus)), 1)
|
||||
|
||||
|
||||
def write_existence_flags(dev):
|
||||
# Generate #defines of the form
|
||||
#
|
||||
# #define DT_<COMPAT>_<INSTANCE> 1
|
||||
#
|
||||
# These are flags for which devices exist.
|
||||
|
||||
for compat in dev.compats:
|
||||
out("{}_{}".format(str2ident(compat), dev.instance_no[compat]), 1)
|
||||
|
||||
|
||||
def reg_addr_ident(reg):
|
||||
# Returns the identifier (e.g., macro name) to be used for the address of
|
||||
# 'reg' in the output
|
||||
|
||||
dev = reg.dev
|
||||
|
||||
# NOTE: to maintain compat wit the old script we special case if there's
|
||||
# only a single register (we drop the '_0').
|
||||
if len(dev.regs) > 1:
|
||||
return "BASE_ADDRESS_{}".format(dev.regs.index(reg))
|
||||
else:
|
||||
return "BASE_ADDRESS"
|
||||
|
||||
|
||||
def reg_size_ident(reg):
|
||||
# Returns the identifier (e.g., macro name) to be used for the size of
|
||||
# 'reg' in the output
|
||||
|
||||
dev = reg.dev
|
||||
|
||||
# NOTE: to maintain compat wit the old script we special case if there's
|
||||
# only a single register (we drop the '_0').
|
||||
if len(dev.regs) > 1:
|
||||
return "SIZE_{}".format(dev.regs.index(reg))
|
||||
else:
|
||||
return "SIZE"
|
||||
|
||||
|
||||
def dev_ident(dev):
|
||||
# Returns an identifier for the Device 'dev'. Used when building e.g. macro
|
||||
# names.
|
||||
|
||||
# TODO: Handle PWM on STM
|
||||
# TODO: Better document the rules of how we generate things
|
||||
|
||||
ident = ""
|
||||
|
||||
if dev.bus:
|
||||
ident += "{}_{:X}_".format(
|
||||
str2ident(dev.parent.matching_compat), dev.parent.unit_addr)
|
||||
|
||||
ident += "{}_".format(str2ident(dev.matching_compat))
|
||||
|
||||
if dev.unit_addr is not None:
|
||||
ident += "{:X}".format(dev.unit_addr)
|
||||
elif dev.parent.unit_addr is not None:
|
||||
ident += "{:X}_{}".format(dev.parent.unit_addr, str2ident(dev.name))
|
||||
else:
|
||||
# This is a bit of a hack
|
||||
ident += "{}".format(str2ident(dev.name))
|
||||
|
||||
return ident
|
||||
|
||||
|
||||
def dev_aliases(dev):
|
||||
# Returns a list of aliases for the Device 'dev', used e.g. when building
|
||||
# macro names
|
||||
|
||||
return dev_path_aliases(dev) + dev_instance_aliases(dev)
|
||||
|
||||
|
||||
def dev_path_aliases(dev):
|
||||
# Returns a list of aliases for the Device 'dev', based on the aliases
|
||||
# registered for the device, in the /aliases node. Used when building e.g.
|
||||
# macro names.
|
||||
|
||||
if dev.matching_compat is None:
|
||||
return []
|
||||
|
||||
compat_s = str2ident(dev.matching_compat)
|
||||
|
||||
aliases = []
|
||||
for alias in dev.aliases:
|
||||
aliases.append("ALIAS_{}".format(str2ident(alias)))
|
||||
# TODO: See if we can remove or deprecate this form
|
||||
aliases.append("{}_{}".format(compat_s, str2ident(alias)))
|
||||
|
||||
return aliases
|
||||
|
||||
|
||||
def dev_instance_aliases(dev):
|
||||
# Returns a list of aliases for the Device 'dev', based on the instance
|
||||
# number of the device (based on how many instances of that particular
|
||||
# device there are).
|
||||
#
|
||||
# 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]
|
||||
|
||||
|
||||
def write_addr_size(edt, prop_name, prefix):
|
||||
# Writes <prefix>_BASE_ADDRESS and <prefix>_SIZE for the device
|
||||
# pointed at by the /chosen property named 'prop_name', if it exists
|
||||
|
||||
dev = edt.chosen_dev(prop_name)
|
||||
if not dev:
|
||||
return
|
||||
|
||||
if not dev.regs:
|
||||
err("missing 'reg' property in node pointed at by /chosen/{} ({!r})"
|
||||
.format(prop_name, dev))
|
||||
|
||||
out_comment("/chosen/{} ({})".format(prop_name, dev.path))
|
||||
out("{}_BASE_ADDRESS".format(prefix), hex(dev.regs[0].addr))
|
||||
out("{}_SIZE".format(prefix), dev.regs[0].size//1024)
|
||||
|
||||
|
||||
def write_flash(flash_dev):
|
||||
# Writes output for the node pointed at by the zephyr,flash property in
|
||||
# /chosen
|
||||
|
||||
out_comment("/chosen/zephyr,flash ({})"
|
||||
.format(flash_dev.path if flash_dev else "missing"))
|
||||
|
||||
if not flash_dev:
|
||||
# No flash device. Write dummy values.
|
||||
out("FLASH_BASE_ADDRESS", 0)
|
||||
out("FLASH_SIZE", 0)
|
||||
return
|
||||
|
||||
if len(flash_dev.regs) != 1:
|
||||
err("expected zephyr,flash to have a single register, has {}"
|
||||
.format(len(flash_dev.regs)))
|
||||
|
||||
if flash_dev.bus == "spi" and len(flash_dev.parent.regs) == 2:
|
||||
reg = flash_dev.parent.regs[1] # QSPI flash
|
||||
else:
|
||||
reg = flash_dev.regs[0]
|
||||
|
||||
out("FLASH_BASE_ADDRESS", hex(reg.addr))
|
||||
if reg.size:
|
||||
out("FLASH_SIZE", reg.size//1024)
|
||||
|
||||
if "erase-block-size" in flash_dev.props:
|
||||
out("FLASH_ERASE_BLOCK_SIZE", flash_dev.props["erase-block-size"].val)
|
||||
|
||||
if "write-block-size" in flash_dev.props:
|
||||
out("FLASH_WRITE_BLOCK_SIZE", flash_dev.props["write-block-size"].val)
|
||||
|
||||
|
||||
def write_code_partition(code_dev):
|
||||
# Writes output for the node pointed at by the zephyr,code-partition
|
||||
# property in /chosen
|
||||
|
||||
out_comment("/chosen/zephyr,code-partition ({})"
|
||||
.format(code_dev.path if code_dev else "missing"))
|
||||
|
||||
if not code_dev:
|
||||
# No code partition. Write dummy values.
|
||||
out("CODE_PARTITION_OFFSET", 0)
|
||||
out("CODE_PARTITION_SIZE", 0)
|
||||
return
|
||||
|
||||
if not code_dev.regs:
|
||||
err("missing 'regs' property on {!r}".format(code_dev))
|
||||
|
||||
out("CODE_PARTITION_OFFSET", code_dev.regs[0].addr)
|
||||
out("CODE_PARTITION_SIZE", code_dev.regs[0].size)
|
||||
|
||||
|
||||
def write_flash_partition(partition_dev, index):
|
||||
out_comment("Flash partition at " + partition_dev.path)
|
||||
|
||||
if partition_dev.label is None:
|
||||
err("missing 'label' property on {!r}".format(partition_dev))
|
||||
|
||||
# Generate label-based identifiers
|
||||
write_flash_partition_prefix(
|
||||
"FLASH_AREA_" + str2ident(partition_dev.label), partition_dev, index)
|
||||
|
||||
# Generate index-based identifiers
|
||||
write_flash_partition_prefix(
|
||||
"FLASH_AREA_{}".format(index), partition_dev, index)
|
||||
|
||||
|
||||
def write_flash_partition_prefix(prefix, partition_dev, index):
|
||||
# write_flash_partition() helper. Generates identifiers starting with
|
||||
# 'prefix'.
|
||||
|
||||
out("{}_ID".format(prefix), index)
|
||||
|
||||
out("{}_READ_ONLY".format(prefix), 1 if partition_dev.read_only else 0)
|
||||
|
||||
for i, reg in enumerate(partition_dev.regs):
|
||||
# Also add aliases that point to the first sector (TODO: get rid of the
|
||||
# aliases?)
|
||||
out("{}_OFFSET_{}".format(prefix, i), reg.addr,
|
||||
aliases=["{}_OFFSET".format(prefix)] if i == 0 else [])
|
||||
out("{}_SIZE_{}".format(prefix, i), reg.size,
|
||||
aliases=["{}_SIZE".format(prefix)] if i == 0 else [])
|
||||
|
||||
controller = partition_dev.flash_controller
|
||||
if controller.label is not None:
|
||||
out_s("{}_DEV".format(prefix), controller.label)
|
||||
|
||||
|
||||
def write_required_label(ident, dev):
|
||||
# Helper function. Writes '#define <ident> "<label>"', where <label>
|
||||
# is the value of the 'label' property from 'dev'. Does nothing if
|
||||
# 'dev' is None.
|
||||
#
|
||||
# Errors out if 'dev' exists but has no label.
|
||||
|
||||
if not dev:
|
||||
return
|
||||
|
||||
if dev.label is None:
|
||||
err("missing 'label' property on {!r}".format(dev))
|
||||
|
||||
out_s(ident, dev.label)
|
||||
|
||||
|
||||
def write_irqs(dev):
|
||||
# Writes IRQ num and data for the interrupts in dev's 'interrupt' property
|
||||
|
||||
def irq_name_alias(irq, cell_name):
|
||||
if not irq.name:
|
||||
return None
|
||||
|
||||
alias = "IRQ_{}".format(str2ident(irq.name))
|
||||
if cell_name != "irq":
|
||||
alias += "_" + str2ident(cell_name)
|
||||
return alias
|
||||
|
||||
for irq_i, irq in enumerate(dev.interrupts):
|
||||
# We ignore the controller for now
|
||||
for cell_name, cell_value in irq.specifier.items():
|
||||
ident = "IRQ_{}".format(irq_i)
|
||||
if cell_name != "irq":
|
||||
ident += "_" + str2ident(cell_name)
|
||||
|
||||
out_dev(dev, ident, cell_value,
|
||||
name_alias=irq_name_alias(irq, cell_name))
|
||||
|
||||
|
||||
def write_gpios(dev):
|
||||
# Writes GPIO controller data for the gpios in dev's 'gpios' property
|
||||
|
||||
for gpios in dev.gpios.values():
|
||||
for gpio_i, gpio in enumerate(gpios):
|
||||
write_gpio(dev, gpio, gpio_i if len(gpios) > 1 else None)
|
||||
|
||||
|
||||
def write_gpio(dev, gpio, index=None):
|
||||
# Writes GPIO controller & data for the GPIO object 'gpio'. If 'index' is
|
||||
# not None, it is added as a suffix to identifiers.
|
||||
|
||||
ctrl_ident = "GPIOS_CONTROLLER"
|
||||
if gpio.name:
|
||||
ctrl_ident = str2ident(gpio.name) + "_" + ctrl_ident
|
||||
if index is not None:
|
||||
ctrl_ident += "_{}".format(index)
|
||||
|
||||
out_dev_s(dev, ctrl_ident, gpio.controller.label)
|
||||
|
||||
for cell, val in gpio.specifier.items():
|
||||
cell_ident = "GPIOS_" + str2ident(cell)
|
||||
if gpio.name:
|
||||
cell_ident = str2ident(gpio.name) + "_" + cell_ident
|
||||
if index is not None:
|
||||
cell_ident += "_{}".format(index)
|
||||
|
||||
out_dev(dev, cell_ident, val)
|
||||
|
||||
|
||||
def write_spi_dev(dev):
|
||||
# Writes SPI device GPIO chip select data if there is any
|
||||
|
||||
cs_gpio = edtlib.spi_dev_cs_gpio(dev)
|
||||
if cs_gpio is not None:
|
||||
write_gpio(dev, cs_gpio)
|
||||
|
||||
|
||||
def write_pwms(dev):
|
||||
# Writes PWM controller and specifier info for the PWMs in dev's 'pwms'
|
||||
# property
|
||||
|
||||
for pwm in dev.pwms:
|
||||
if pwm.controller.label is not None:
|
||||
out_dev_s(dev, "PWMS_CONTROLLER", pwm.controller.label)
|
||||
|
||||
for spec, val in pwm.specifier.items():
|
||||
out_dev(dev, "PWMS_" + str2ident(spec), val)
|
||||
|
||||
|
||||
def write_clocks(dev):
|
||||
# Writes clock controller and specifier info for the clock in dev's 'clock'
|
||||
# property
|
||||
|
||||
for clock_i, clock in enumerate(dev.clocks):
|
||||
if clock.controller.label is not None:
|
||||
out_dev_s(dev, "CLOCK_CONTROLLER", clock.controller.label)
|
||||
|
||||
if clock.frequency is not None:
|
||||
out_dev(dev, "CLOCKS_CLOCK_FREQUENCY", clock.frequency)
|
||||
|
||||
for spec, val in clock.specifier.items():
|
||||
if clock_i == 0:
|
||||
clk_name_alias = "CLOCK_" + str2ident(spec)
|
||||
else:
|
||||
clk_name_alias = None
|
||||
|
||||
out_dev(dev, "CLOCK_{}_{}".format(str2ident(spec), clock_i), val,
|
||||
name_alias=clk_name_alias)
|
||||
|
||||
|
||||
def str2ident(s):
|
||||
# Converts 's' to a form suitable for (part of) an identifier
|
||||
|
||||
return s.replace("-", "_") \
|
||||
.replace(",", "_") \
|
||||
.replace("@", "_") \
|
||||
.replace("/", "_") \
|
||||
.replace("+", "PLUS") \
|
||||
.upper()
|
||||
|
||||
|
||||
def out_dev(dev, ident, val, name_alias=None):
|
||||
# Writes an
|
||||
#
|
||||
# <device prefix>_<ident> = <val>
|
||||
#
|
||||
# assignment, along with a set of
|
||||
#
|
||||
# <device alias>_<ident>
|
||||
#
|
||||
# aliases, for each device alias. If 'name_alias' (a string) is passed,
|
||||
# then these additional aliases are generated:
|
||||
#
|
||||
# <device prefix>_<name alias>
|
||||
# <device alias>_<name alias> (for each device alias)
|
||||
#
|
||||
# 'name_alias' is used for reg-names and the like.
|
||||
|
||||
dev_prefix = dev_ident(dev)
|
||||
|
||||
aliases = [alias + "_" + ident for alias in dev_aliases(dev)]
|
||||
if name_alias is not None:
|
||||
aliases.append(dev_prefix + "_" + name_alias)
|
||||
aliases += [alias + "_" + name_alias for alias in dev_aliases(dev)]
|
||||
|
||||
out(dev_prefix + "_" + ident, val, aliases)
|
||||
|
||||
|
||||
def out_dev_s(dev, ident, s):
|
||||
# Like out_dev(), but puts quotes around 's' and escapes any double quotes
|
||||
# and backslashes within it
|
||||
|
||||
# \ must be escaped before " to avoid double escaping
|
||||
out_dev(dev, ident, '"{}"'.format(escape(s)))
|
||||
|
||||
|
||||
def out_s(ident, val):
|
||||
# Like out(), but puts quotes around 's' and escapes any double quotes and
|
||||
# backslashes within it
|
||||
|
||||
out(ident, '"{}"'.format(escape(val)))
|
||||
|
||||
|
||||
def out(ident, val, aliases=()):
|
||||
# Writes '#define <ident> <val>' to the header and '<ident>=<val>' to the
|
||||
# the configuration file.
|
||||
#
|
||||
# Also writes any aliases listed in 'aliases' (an iterable). For the
|
||||
# header, these look like '#define <alias> <ident>'. For the configuration
|
||||
# file, the value is just repeated as '<alias>=<val>' for each alias.
|
||||
|
||||
print("#define DT_{:40} {}".format(ident, val), file=header_file)
|
||||
print("DT_{}={}".format(ident, val), file=conf_file)
|
||||
|
||||
for alias in aliases:
|
||||
if alias != ident:
|
||||
print("#define DT_{:40} DT_{}".format(alias, ident),
|
||||
file=header_file)
|
||||
# For the configuration file, the value is just repeated for all
|
||||
# the aliases
|
||||
print("DT_{}={}".format(alias, val), file=conf_file)
|
||||
|
||||
|
||||
def out_comment(s, blank_before=True):
|
||||
# Writes 's' as a comment to the header and configuration file. 's' is
|
||||
# allowed to have multiple lines. blank_before=True adds a blank line
|
||||
# before the comment.
|
||||
|
||||
if blank_before:
|
||||
print(file=header_file)
|
||||
print(file=conf_file)
|
||||
|
||||
# Double-space in header for readability
|
||||
print("/* " + s + " */", file=header_file)
|
||||
print("\n".join("# " + line for line in s.splitlines()), file=conf_file)
|
||||
|
||||
|
||||
def escape(s):
|
||||
# Backslash-escapes any double quotes and backslashes in 's'
|
||||
|
||||
# \ must be escaped before " to avoid double escaping
|
||||
return s.replace("\\", "\\\\").replace('"', '\\"')
|
||||
|
||||
|
||||
def err(s):
|
||||
raise Exception(s)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
14
scripts/dts/test-bindings/child.yaml
Normal file
14
scripts/dts/test-bindings/child.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Child binding
|
||||
description: Child binding
|
||||
|
||||
inherits:
|
||||
!include grandchild.yaml
|
||||
|
||||
properties:
|
||||
bar:
|
||||
category: required
|
||||
type: int
|
14
scripts/dts/test-bindings/clock-1-cell.yaml
Normal file
14
scripts/dts/test-bindings/clock-1-cell.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Clock source with one cell
|
||||
description: Clock source with one cell
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "clock-one-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
15
scripts/dts/test-bindings/clock-2-cell.yaml
Normal file
15
scripts/dts/test-bindings/clock-2-cell.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Clock source with two cells
|
||||
description: Clock source with two cells
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "clock-two-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
||||
- two
|
14
scripts/dts/test-bindings/fixed-clock.yaml
Normal file
14
scripts/dts/test-bindings/fixed-clock.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Fixed clock
|
||||
description: Fixed clock
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "fixed-clock"
|
||||
type: string-array
|
||||
|
||||
clock-frequency:
|
||||
type: int
|
14
scripts/dts/test-bindings/gpio-1-cell.yaml
Normal file
14
scripts/dts/test-bindings/gpio-1-cell.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: GPIO controller with one cell
|
||||
description: GPIO controller with one cell
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "gpio-one-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
15
scripts/dts/test-bindings/gpio-2-cell.yaml
Normal file
15
scripts/dts/test-bindings/gpio-2-cell.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: GPIO controller with two cells
|
||||
description: GPIO controller with two cells
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "gpio-two-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
||||
- two
|
15
scripts/dts/test-bindings/grandchild.yaml
Normal file
15
scripts/dts/test-bindings/grandchild.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Grandchild binding
|
||||
description: Grandchild binding
|
||||
|
||||
properties:
|
||||
foo:
|
||||
category: optional
|
||||
type: int
|
||||
|
||||
baz:
|
||||
category: required
|
||||
type: int
|
14
scripts/dts/test-bindings/interrupt-1-cell.yaml
Normal file
14
scripts/dts/test-bindings/interrupt-1-cell.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Interrupt controller with one cell
|
||||
description: Interrupt controller with one cell
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "interrupt-one-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
15
scripts/dts/test-bindings/interrupt-2-cell.yaml
Normal file
15
scripts/dts/test-bindings/interrupt-2-cell.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Interrupt controller with two cells
|
||||
description: Interrupt controller with two cells
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "interrupt-two-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
||||
- two
|
16
scripts/dts/test-bindings/interrupt-3-cell.yaml
Normal file
16
scripts/dts/test-bindings/interrupt-3-cell.yaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Interrupt controller with three cells
|
||||
description: Interrupt controller with three cells
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "interrupt-three-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
||||
- two
|
||||
- three
|
19
scripts/dts/test-bindings/parent.yaml
Normal file
19
scripts/dts/test-bindings/parent.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Parent binding
|
||||
description: Parent binding
|
||||
|
||||
inherits:
|
||||
!include child.yaml
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "binding-include-test"
|
||||
type: string-array
|
||||
|
||||
foo:
|
||||
# Changed from "optional" in grandchild.yaml
|
||||
category: required
|
||||
# Type set in grandchild
|
26
scripts/dts/test-bindings/props.yaml
Normal file
26
scripts/dts/test-bindings/props.yaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Device.props test
|
||||
description: Device.props test
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "props"
|
||||
type: string-array
|
||||
|
||||
int:
|
||||
type: int
|
||||
|
||||
array:
|
||||
type: array
|
||||
|
||||
uint8-array:
|
||||
type: uint8-array
|
||||
|
||||
string:
|
||||
type: string
|
||||
|
||||
string-array:
|
||||
type: string-array
|
11
scripts/dts/test-bindings/pwm-0-cell.yaml
Normal file
11
scripts/dts/test-bindings/pwm-0-cell.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: PWM source with zero cells
|
||||
description: PWM source with zero cells
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "pwm-zero-cell"
|
||||
type: string-array
|
14
scripts/dts/test-bindings/pwm-1-cell.yaml
Normal file
14
scripts/dts/test-bindings/pwm-1-cell.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: PWM source with one cell
|
||||
description: PWM source with one cell
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "pwm-one-cell"
|
||||
type: string-array
|
||||
|
||||
"#cells":
|
||||
- one
|
21
scripts/dts/test-bindings/sub-node-parent.yaml
Normal file
21
scripts/dts/test-bindings/sub-node-parent.yaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
title: Sub-node test
|
||||
description: Sub-node test
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "parent-with-sub-node"
|
||||
type: string-array
|
||||
|
||||
sub-node:
|
||||
properties:
|
||||
foo:
|
||||
required: true
|
||||
type: int
|
||||
|
||||
bar:
|
||||
required: True
|
||||
type: int
|
299
scripts/dts/test.dts
Normal file
299
scripts/dts/test.dts
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Nordic Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Used by testedtlib.py
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
//
|
||||
// Interrupts
|
||||
//
|
||||
|
||||
interrupt-parent-test {
|
||||
controller {
|
||||
compatible = "interrupt-three-cell";
|
||||
#interrupt-cells = <3>;
|
||||
interrupt-controller;
|
||||
};
|
||||
node {
|
||||
interrupts = <1 2 3 4 5 6>;
|
||||
interrupt-names = "foo", "bar";
|
||||
interrupt-parent = <&{/interrupt-parent-test/controller}>;
|
||||
};
|
||||
};
|
||||
interrupts-extended-test {
|
||||
controller-0 {
|
||||
compatible = "interrupt-one-cell";
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-controller;
|
||||
};
|
||||
controller-1 {
|
||||
compatible = "interrupt-two-cell";
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
};
|
||||
controller-2 {
|
||||
compatible = "interrupt-three-cell";
|
||||
#interrupt-cells = <3>;
|
||||
interrupt-controller;
|
||||
};
|
||||
node {
|
||||
interrupts-extended = <
|
||||
&{/interrupts-extended-test/controller-0} 1
|
||||
&{/interrupts-extended-test/controller-1} 2 3
|
||||
&{/interrupts-extended-test/controller-2} 4 5 6>;
|
||||
};
|
||||
};
|
||||
interrupt-map-test {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
|
||||
controller-0 {
|
||||
compatible = "interrupt-one-cell";
|
||||
#address-cells = <1>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-controller;
|
||||
};
|
||||
controller-1 {
|
||||
compatible = "interrupt-two-cell";
|
||||
#address-cells = <2>;
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
};
|
||||
controller-2 {
|
||||
compatible = "interrupt-three-cell";
|
||||
#address-cells = <3>;
|
||||
#interrupt-cells = <3>;
|
||||
interrupt-controller;
|
||||
};
|
||||
nexus {
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-map = <
|
||||
0 0 0 0 &{/interrupt-map-test/controller-0} 0 0
|
||||
0 0 0 1 &{/interrupt-map-test/controller-1} 0 0 0 1
|
||||
0 0 0 2 &{/interrupt-map-test/controller-2} 0 0 0 0 0 2
|
||||
0 1 0 0 &{/interrupt-map-test/controller-0} 0 3
|
||||
0 1 0 1 &{/interrupt-map-test/controller-1} 0 0 0 4
|
||||
0 1 0 2 &{/interrupt-map-test/controller-2} 0 0 0 0 0 5>;
|
||||
};
|
||||
node@0 {
|
||||
reg = <0 0>;
|
||||
interrupts = <0 0 0 1 0 2>;
|
||||
interrupt-parent = <&{/interrupt-map-test/nexus}>;
|
||||
};
|
||||
node@1 {
|
||||
reg = <0 1>;
|
||||
interrupts-extended = <
|
||||
&{/interrupt-map-test/nexus} 0 0
|
||||
&{/interrupt-map-test/nexus} 0 1
|
||||
&{/interrupt-map-test/nexus} 0 2>;
|
||||
};
|
||||
};
|
||||
interrupt-map-bitops-test {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
|
||||
controller {
|
||||
compatible = "interrupt-two-cell";
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
};
|
||||
nexus {
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-map = <
|
||||
6 6 6 6 &{/interrupt-map-bitops-test/controller} 2 1
|
||||
>;
|
||||
interrupt-map-mask = <0xE 0x7 0xE 0x7>;
|
||||
// Not specified in the DT spec., but shows up due to
|
||||
// common code with GPIO. Might as well test it here.
|
||||
interrupt-map-pass-thru = <1 2 3 3>;
|
||||
};
|
||||
// Child unit specifier: 00000007 0000000E 00000007 0000000E
|
||||
// Mask: 0000000E 00000007 0000000E 00000007
|
||||
// Pass-thru: 00000001 00000002 00000003 00000003
|
||||
node@70000000E {
|
||||
reg = <0x7 0xE>;
|
||||
interrupt-parent = <&{/interrupt-map-bitops-test/nexus}>;
|
||||
interrupts = <0x7 0xE>;
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// GPIOS
|
||||
//
|
||||
|
||||
gpio-test {
|
||||
controller-1 {
|
||||
compatible = "gpio-two-cell";
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
};
|
||||
node {
|
||||
gpios = <&{/gpio-test/controller-0} 1
|
||||
&{/gpio-test/controller-1} 2 3>;
|
||||
foo-gpios = <&{/gpio-test/controller-1} 4 5>;
|
||||
bar-gpios = <&{/gpio-test/controller-1} 6 7>;
|
||||
};
|
||||
// Putting this controller last gives us some coverage for ordering
|
||||
// issues during initialization
|
||||
controller-0 {
|
||||
compatible = "gpio-one-cell";
|
||||
#gpio-cells = <1>;
|
||||
gpio-controller;
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// Clocks
|
||||
//
|
||||
|
||||
clock-test {
|
||||
fixed-clock {
|
||||
// 'fixed-clock' is currently special-cased in the code
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <123>;
|
||||
};
|
||||
clock-1 {
|
||||
compatible = "clock-one-cell";
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
clock-2 {
|
||||
compatible = "clock-two-cell";
|
||||
#clock-cells = <2>;
|
||||
};
|
||||
node {
|
||||
clocks = <&{/clock-test/fixed-clock}
|
||||
&{/clock-test/clock-1} 1
|
||||
&{/clock-test/clock-2} 1 2>;
|
||||
clock-names = "fixed", "one-cell", "two-cell";
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// PWMs
|
||||
//
|
||||
|
||||
pwm-test {
|
||||
pwm-0 {
|
||||
compatible = "pwm-zero-cell";
|
||||
#pwm-cells = <0>;
|
||||
};
|
||||
pwm-1 {
|
||||
compatible = "pwm-one-cell";
|
||||
#pwm-cells = <1>;
|
||||
};
|
||||
node {
|
||||
pwms = <&{/pwm-test/pwm-0}
|
||||
&{/pwm-test/pwm-1} 1>;
|
||||
pwm-names = "zero-cell", "one-cell";
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// 'reg'
|
||||
//
|
||||
|
||||
reg-zero-address-cells {
|
||||
#address-cells = <0>;
|
||||
#size-cells = <1>;
|
||||
|
||||
node {
|
||||
reg = <1 2>;
|
||||
};
|
||||
};
|
||||
reg-zero-size-cells {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
node {
|
||||
reg = <1 2>;
|
||||
};
|
||||
};
|
||||
// Use implied #size-cells = <1>
|
||||
reg-ranges {
|
||||
#address-cells = <2>;
|
||||
|
||||
parent {
|
||||
#address-cells = <1>;
|
||||
ranges = <1 0xA 0xB 1 /* 1 -> 0xA 0xB */
|
||||
2 0xC 0xD 2 /* 2..3 -> 0xC 0xD */
|
||||
4 0xE 0xF 1 /* 4 -> 0xE 0xF */
|
||||
>;
|
||||
|
||||
node {
|
||||
reg = <5 1 /* Matches no range */
|
||||
4 1 /* Matches third range */
|
||||
3 1 /* Matches second range */
|
||||
2 1 /* Matches second range */
|
||||
1 1 /* Matches first range */
|
||||
0 1 /* Matches no range */
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Build up <3 2 1> address with nested 'ranges'
|
||||
reg-nested-ranges {
|
||||
#address-cells = <3>;
|
||||
|
||||
grandparent {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges = <0 0 3 0 0 2 2>;
|
||||
|
||||
parent {
|
||||
#address-cells = <1>;
|
||||
ranges = <0 2 0 2>;
|
||||
|
||||
node {
|
||||
reg = <1 1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// !include in bindings
|
||||
//
|
||||
|
||||
binding-include {
|
||||
compatible = "binding-include-test";
|
||||
foo = <0>;
|
||||
bar = <1>;
|
||||
baz = <2>;
|
||||
};
|
||||
|
||||
//
|
||||
// For testing Device.props (derived from 'properties:' in the binding)
|
||||
//
|
||||
|
||||
props {
|
||||
compatible = "props";
|
||||
int = <1>;
|
||||
array = <1 2 3>;
|
||||
uint8-array = [ 12 34 ];
|
||||
string = "foo";
|
||||
string-array = "foo", "bar", "baz";
|
||||
// Does not appear in the binding, so won't create an entry in
|
||||
// Device.props
|
||||
not-speced = <0>;
|
||||
};
|
||||
|
||||
//
|
||||
// Parent with 'sub-node:' in binding
|
||||
//
|
||||
|
||||
parent-with-sub-node {
|
||||
compatible = "parent-with-sub-node";
|
||||
node {
|
||||
foo = <1>;
|
||||
bar = <2>;
|
||||
not-speced = <0>;
|
||||
};
|
||||
};
|
||||
};
|
2025
scripts/dts/testdtlib.py
Executable file
2025
scripts/dts/testdtlib.py
Executable file
File diff suppressed because it is too large
Load diff
123
scripts/dts/testedtlib.py
Executable file
123
scripts/dts/testedtlib.py
Executable file
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import edtlib
|
||||
|
||||
# Test suite for edtlib.py. Mostly uses string comparisons via the various
|
||||
# __repr__() methods. Can be run directly as an executable.
|
||||
#
|
||||
# This script expects to be run from the directory its in. This simplifies
|
||||
# things, as paths in the output can be assumed below.
|
||||
#
|
||||
# test.dts is the test file, and test-bindings/ has bindings.
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Runs all edtlib tests. Immediately exits with status 1 and a message on
|
||||
stderr on test suite failures.
|
||||
"""
|
||||
|
||||
def fail(msg):
|
||||
raise Exception("test failed: " + msg)
|
||||
|
||||
def verify_streq(actual, expected):
|
||||
actual = str(actual)
|
||||
if actual != expected:
|
||||
# Put values on separate lines to make it easy to spot differences
|
||||
fail("not equal (expected value last):\n'{}'\n'{}'"
|
||||
.format(actual, expected))
|
||||
|
||||
edt = edtlib.EDT("test.dts", "test-bindings")
|
||||
|
||||
#
|
||||
# Test interrupts
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/interrupt-parent-test/node").interrupts,
|
||||
"[<Interrupt, name: foo, target: <Device /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, specifier: {'one': 1, 'two': 2, 'three': 3}>, <Interrupt, name: bar, target: <Device /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, specifier: {'one': 4, 'two': 5, 'three': 6}>]")
|
||||
|
||||
verify_streq(edt.get_dev("/interrupts-extended-test/node").interrupts,
|
||||
"[<Interrupt, target: <Device /interrupts-extended-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, specifier: {'one': 1}>, <Interrupt, target: <Device /interrupts-extended-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, specifier: {'one': 2, 'two': 3}>, <Interrupt, target: <Device /interrupts-extended-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, specifier: {'one': 4, 'two': 5, 'three': 6}>]")
|
||||
|
||||
verify_streq(edt.get_dev("/interrupt-map-test/node@0").interrupts,
|
||||
"[<Interrupt, target: <Device /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, specifier: {'one': 0}>, <Interrupt, target: <Device /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, specifier: {'one': 0, 'two': 1}>, <Interrupt, target: <Device /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, specifier: {'one': 0, 'two': 0, 'three': 2}>]")
|
||||
|
||||
verify_streq(edt.get_dev("/interrupt-map-test/node@1").interrupts,
|
||||
"[<Interrupt, target: <Device /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, specifier: {'one': 3}>, <Interrupt, target: <Device /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, specifier: {'one': 0, 'two': 4}>, <Interrupt, target: <Device /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, specifier: {'one': 0, 'two': 0, 'three': 5}>]")
|
||||
|
||||
verify_streq(edt.get_dev("/interrupt-map-bitops-test/node@70000000E").interrupts,
|
||||
"[<Interrupt, target: <Device /interrupt-map-bitops-test/controller in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, specifier: {'one': 3, 'two': 2}>]")
|
||||
|
||||
#
|
||||
# Test GPIOs
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/gpio-test/node").gpios,
|
||||
"{'': [<GPIO, name: , target: <Device /gpio-test/controller-0 in 'test.dts', binding test-bindings/gpio-1-cell.yaml>, specifier: {'one': 1}>, <GPIO, name: , target: <Device /gpio-test/controller-1 in 'test.dts', binding test-bindings/gpio-2-cell.yaml>, specifier: {'one': 2, 'two': 3}>], 'foo': [<GPIO, name: foo, target: <Device /gpio-test/controller-1 in 'test.dts', binding test-bindings/gpio-2-cell.yaml>, specifier: {'one': 4, 'two': 5}>], 'bar': [<GPIO, name: bar, target: <Device /gpio-test/controller-1 in 'test.dts', binding test-bindings/gpio-2-cell.yaml>, specifier: {'one': 6, 'two': 7}>]}")
|
||||
|
||||
#
|
||||
# Test clocks
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/clock-test/node").clocks,
|
||||
"[<Clock, name: fixed, frequency: 123, target: <Device /clock-test/fixed-clock in 'test.dts', binding test-bindings/fixed-clock.yaml>, specifier: {}>, <Clock, name: one-cell, target: <Device /clock-test/clock-1 in 'test.dts', binding test-bindings/clock-1-cell.yaml>, specifier: {'one': 1}>, <Clock, name: two-cell, target: <Device /clock-test/clock-2 in 'test.dts', binding test-bindings/clock-2-cell.yaml>, specifier: {'one': 1, 'two': 2}>]")
|
||||
|
||||
#
|
||||
# Test PWMs
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/pwm-test/node").pwms,
|
||||
"[<PWM, name: zero-cell, target: <Device /pwm-test/pwm-0 in 'test.dts', binding test-bindings/pwm-0-cell.yaml>, specifier: {}>, <PWM, name: one-cell, target: <Device /pwm-test/pwm-1 in 'test.dts', binding test-bindings/pwm-1-cell.yaml>, specifier: {'one': 1}>]")
|
||||
|
||||
#
|
||||
# Test 'reg'
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/reg-zero-address-cells/node").regs,
|
||||
"[<Register, addr: 0x0, size: 0x1>, <Register, addr: 0x0, size: 0x2>]")
|
||||
|
||||
verify_streq(edt.get_dev("/reg-zero-size-cells/node").regs,
|
||||
"[<Register, addr: 0x1, size: 0x0>, <Register, addr: 0x2, size: 0x0>]")
|
||||
|
||||
verify_streq(edt.get_dev("/reg-ranges/parent/node").regs,
|
||||
"[<Register, addr: 0x5, size: 0x1>, <Register, addr: 0xe0000000f, size: 0x1>, <Register, addr: 0xc0000000e, size: 0x1>, <Register, addr: 0xc0000000d, size: 0x1>, <Register, addr: 0xa0000000b, size: 0x1>, <Register, addr: 0x0, size: 0x1>]")
|
||||
|
||||
verify_streq(edt.get_dev("/reg-nested-ranges/grandparent/parent/node").regs,
|
||||
"[<Register, addr: 0x30000000200000001, size: 0x1>]")
|
||||
|
||||
#
|
||||
# Test !include in bindings
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/binding-include").description,
|
||||
"Parent binding")
|
||||
|
||||
verify_streq(edt.get_dev("/binding-include").props,
|
||||
"{'compatible': <Property, name: compatible, value: ['binding-include-test']>, 'foo': <Property, name: foo, value: 0>, 'bar': <Property, name: bar, value: 1>, 'baz': <Property, name: baz, value: 2>}")
|
||||
|
||||
#
|
||||
# Test 'sub-node:' in binding
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/parent-with-sub-node/node").description,
|
||||
"Sub-node test")
|
||||
|
||||
verify_streq(edt.get_dev("/parent-with-sub-node/node").props,
|
||||
"{'foo': <Property, name: foo, value: 1>, 'bar': <Property, name: bar, value: 2>}")
|
||||
|
||||
#
|
||||
# Test Device.property (derived from DT and 'properties:' in the binding)
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/props").props,
|
||||
r"{'compatible': <Property, name: compatible, value: ['props']>, 'int': <Property, name: int, value: 1>, 'array': <Property, name: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, value: b'\x124'>, 'string': <Property, name: string, value: 'foo'>, 'string-array': <Property, name: string-array, value: ['foo', 'bar', 'baz']>}")
|
||||
|
||||
|
||||
print("all tests passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
Loading…
Reference in a new issue