blob: 87260f962877beba50b88884da137d6f00ae1cb6 [file] [log] [blame]
#
# Copyright (c) 2017 Linaro
# Copyright (c) 2017 Bobby Noelte
#
# SPDX-License-Identifier: Apache-2.0
#
# NOTE: This file is part of the old device tree scripts, which will be removed
# later. They are kept to generate some legacy #defines via the
# --deprecated-only flag.
#
# The new scripts are gen_defines.py, edtlib.py, and dtlib.py.
import sys
from collections import defaultdict
# globals
phandles = {}
aliases = defaultdict(list)
chosen = {}
reduced = {}
defs = {}
bindings = {}
bus_bindings = {}
binding_compats = []
deprecated = []
deprecated_main = []
old_alias_names = False
regs_config = {
'zephyr,sram' : 'DT_SRAM',
'zephyr,ccm' : 'DT_CCM',
'zephyr,dtcm' : 'DT_DTCM'
}
name_config = {
'zephyr,console' : 'DT_UART_CONSOLE_ON_DEV_NAME',
'zephyr,shell-uart' : 'DT_UART_SHELL_ON_DEV_NAME',
'zephyr,bt-uart' : 'DT_BT_UART_ON_DEV_NAME',
'zephyr,bt-c2h-uart' : 'DT_BT_C2H_UART_ON_DEV_NAME',
'zephyr,uart-pipe' : 'DT_UART_PIPE_ON_DEV_NAME',
'zephyr,bt-mon-uart' : 'DT_BT_MONITOR_ON_DEV_NAME',
'zephyr,uart-mcumgr' : 'DT_UART_MCUMGR_ON_DEV_NAME'
}
def str_to_label(s):
# Change ,-@/ to _ and uppercase
return s.replace('-', '_') \
.replace(',', '_') \
.replace('@', '_') \
.replace('/', '_') \
.replace('.', '_') \
.replace('+', 'PLUS') \
.upper()
def all_compats(node):
# Returns a set() of all 'compatible' strings that appear at or below
# 'node', skipping disabled nodes
if node['props'].get('status') == 'disabled':
return set()
compats = set()
if 'compatible' in node['props']:
val = node['props']['compatible']
if isinstance(val, list):
compats.update(val)
else:
compats.add(val)
for child_node in node['children'].values():
compats.update(all_compats(child_node))
return compats
def create_aliases(root):
if 'aliases' in root['children']:
for name, node_path in root['children']['aliases']['props'].items():
aliases[node_path].append(name)
def get_compat(node_path):
# Returns the value of the 'compatible' property for the node at
# 'node_path'. Also checks the node's parent.
#
# Returns None if neither the node nor its parent has a 'compatible'
# property.
compat = reduced[node_path]['props'].get('compatible') or \
reduced[get_parent_path(node_path)]['props'].get('compatible')
if isinstance(compat, list):
return compat[0]
return compat
def create_chosen(root):
if 'chosen' in root['children']:
chosen.update(root['children']['chosen']['props'])
def create_phandles(root, name):
if root['props'].get('status') == 'disabled':
return
if 'phandle' in root['props']:
phandles[root['props']['phandle']] = name
if name != '/':
name += '/'
for child_name, child_node in root['children'].items():
create_phandles(child_node, name + child_name)
def insert_defs(node_path, new_defs, new_aliases):
for key in new_defs:
if key.startswith('DT_COMPAT_'):
node_path = 'compatibles'
remove = [k for k in new_aliases if k in new_defs]
for k in remove: del new_aliases[k]
if node_path in defs:
remove = [k for k in new_aliases if k in defs[node_path]]
for k in remove: del new_aliases[k]
defs[node_path]['aliases'].update(new_aliases)
defs[node_path].update(new_defs)
else:
new_defs['aliases'] = new_aliases
defs[node_path] = new_defs
# Dictionary where all keys default to 0. Used by create_reduced().
last_used_id = defaultdict(int)
def create_reduced(node, path):
# Compress nodes list to nodes w/ paths, add interrupt parent
if node['props'].get('status') == 'disabled':
return
reduced[path] = node.copy()
reduced[path].pop('children', None)
# Assign an instance ID for each compat
compat = node['props'].get('compatible')
if compat:
if not isinstance(compat, list):
compat = [compat]
reduced[path]['instance_id'] = {}
for comp in compat:
reduced[path]['instance_id'][comp] = last_used_id[comp]
last_used_id[comp] += 1
# Flatten 'prop = <1 2>, <3 4>' (which turns into nested lists) to
# 'prop = <1 2 3 4>'
for val in node['props'].values():
if isinstance(val, list) and isinstance(val[0], list):
# In-place modification
val[:] = [item for sublist in val for item in sublist]
if node['children']:
if path != '/':
path += '/'
for child_name, child_node in sorted(node['children'].items()):
create_reduced(child_node, path + child_name)
def node_label(node_path):
node_compat = get_compat(node_path)
def_label = str_to_label(node_compat)
if '@' in node_path:
# See if we have number we can convert
try:
unit_addr = int(node_path.split('@')[-1], 16)
(nr_addr_cells, nr_size_cells) = get_addr_size_cells(node_path)
unit_addr += translate_addr(unit_addr, node_path,
nr_addr_cells, nr_size_cells)
unit_addr = "%x" % unit_addr
except Exception:
unit_addr = node_path.split('@')[-1]
def_label += '_' + str_to_label(unit_addr)
else:
def_label += '_' + str_to_label(node_path.split('/')[-1])
return def_label
def get_parent_path(node_path):
# Turns /foo/bar into /foo. Returns None for /.
if node_path == '/':
return None
return '/'.join(node_path.split('/')[:-1]) or '/'
def find_parent_prop(node_path, prop):
parent_path = get_parent_path(node_path)
if prop not in reduced[parent_path]['props']:
raise Exception("Parent of node " + node_path +
" has no " + prop + " property")
return reduced[parent_path]['props'][prop]
# Get the #{address,size}-cells for a given node
def get_addr_size_cells(node_path):
parent_addr = get_parent_path(node_path)
# The DT spec says that if #address-cells is missing default to 2
# if #size-cells is missing default to 1
nr_addr = reduced[parent_addr]['props'].get('#address-cells', 2)
nr_size = reduced[parent_addr]['props'].get('#size-cells', 1)
return (nr_addr, nr_size)
def translate_addr(addr, node_path, nr_addr_cells, nr_size_cells):
parent_path = get_parent_path(node_path)
ranges = reduced[parent_path]['props'].get('ranges')
if not ranges:
return 0
if isinstance(ranges, list):
ranges = ranges.copy() # Modified in-place below
else:
# Empty value ('ranges;'), meaning the parent and child address spaces
# are the same
ranges = []
nr_p_addr_cells, nr_p_size_cells = get_addr_size_cells(parent_path)
range_offset = 0
while ranges:
child_bus_addr = 0
parent_bus_addr = 0
range_len = 0
for x in range(nr_addr_cells):
val = ranges.pop(0) << (32 * (nr_addr_cells - x - 1))
child_bus_addr += val
for x in range(nr_p_addr_cells):
val = ranges.pop(0) << (32 * (nr_p_addr_cells - x - 1))
parent_bus_addr += val
for x in range(nr_size_cells):
range_len += ranges.pop(0) << (32 * (nr_size_cells - x - 1))
# if we are outside of the range we don't need to translate
if child_bus_addr <= addr <= (child_bus_addr + range_len):
range_offset = parent_bus_addr - child_bus_addr
break
parent_range_offset = translate_addr(addr + range_offset,
parent_path, nr_p_addr_cells, nr_p_size_cells)
range_offset += parent_range_offset
return range_offset
def enable_old_alias_names(enable):
global old_alias_names
old_alias_names = enable
def add_compat_alias(node_path, label_postfix, label, prop_aliases, deprecate=False):
if 'instance_id' in reduced[node_path]:
instance = reduced[node_path]['instance_id']
for k in instance:
i = instance[k]
b = 'DT_' + str_to_label(k) + '_' + str(i) + '_' + label_postfix
deprecated.append(b)
prop_aliases[b] = label
b = "DT_INST_{}_{}_{}".format(str(i), str_to_label(k), label_postfix)
prop_aliases[b] = label
if deprecate:
deprecated.append(b)
def add_prop_aliases(node_path,
alias_label_function, prop_label, prop_aliases, deprecate=False):
node_compat = get_compat(node_path)
new_alias_prefix = 'DT_'
for alias in aliases[node_path]:
old_alias_label = alias_label_function(alias)
new_alias_label = new_alias_prefix + 'ALIAS_' + old_alias_label
new_alias_compat_label = new_alias_prefix + str_to_label(node_compat) + '_' + old_alias_label
if new_alias_label != prop_label:
prop_aliases[new_alias_label] = prop_label
if deprecate:
deprecated.append(new_alias_label)
if new_alias_compat_label != prop_label:
prop_aliases[new_alias_compat_label] = prop_label
if deprecate:
deprecated.append(new_alias_compat_label)
if old_alias_names and old_alias_label != prop_label:
prop_aliases[old_alias_label] = prop_label
def get_binding(node_path):
compat = reduced[node_path]['props'].get('compatible')
if isinstance(compat, list):
compat = compat[0]
parent_path = get_parent_path(node_path)
parent_compat = get_compat(parent_path)
if parent_compat in bindings:
parent_binding = bindings[parent_compat]
# see if we're a sub-node
if compat is None and 'sub-node' in parent_binding:
return parent_binding['sub-node']
# look for a bus-specific binding
if 'child' in parent_binding and 'bus' in parent_binding['child']:
bus = parent_binding['child']['bus']
return bus_bindings[bus][compat]
# No bus-specific binding found, look in the main dict.
if compat:
return bindings[compat]
return None
def get_binding_compats():
return binding_compats
def build_cell_array(prop_array):
index = 0
ret_array = []
while index < len(prop_array):
handle = prop_array[index]
if handle in {0, -1}:
ret_array.append([])
index += 1
else:
# get controller node (referenced via phandle)
cell_parent = phandles[handle]
for prop in reduced[cell_parent]['props']:
if prop[0] == '#' and '-cells' in prop:
num_cells = reduced[cell_parent]['props'][prop]
break
ret_array.append(prop_array[index:index+num_cells+1])
index += num_cells + 1
return ret_array
def child_to_parent_unmap(cell_parent, gpio_index):
# This function returns a (gpio-controller, pin number) tuple from
# cell_parent (identified as a 'nexus node', ie: has a 'gpio-map'
# property) and gpio_index.
# Note: Nexus nodes and gpio-map property are described in the
# upcoming (presumably v0.3) Device Tree specification, chapter
# 'Nexus nodes and Specifier Mapping'.
# First, retrieve gpio-map as a list
gpio_map = reduced[cell_parent]['props']['gpio-map']
# Before parsing, we need to know 'gpio-map' row size
# gpio-map raws are encoded as follows:
# [child specifier][gpio controller phandle][parent specifier]
# child specifier field length is connector property #gpio-cells
child_specifier_size = reduced[cell_parent]['props']['#gpio-cells']
# parent specifier field length is parent property #gpio-cells
# Assumption 1: We assume parent #gpio-cells is constant across
# the map, so we take the value of the first occurrence and apply
# to the whole map.
parent = phandles[gpio_map[child_specifier_size]]
parent_specifier_size = reduced[parent]['props']['#gpio-cells']
array_cell_size = child_specifier_size + 1 + parent_specifier_size
# Now that the length of each entry in 'gpio-map' is known,
# look for a match with gpio_index
for i in range(0, len(gpio_map), array_cell_size):
entry = gpio_map[i:i+array_cell_size]
if entry[0] == gpio_index:
parent_controller_phandle = entry[child_specifier_size]
# Assumption 2: We assume optional properties 'gpio-map-mask'
# and 'gpio-map-pass-thru' are not specified.
# So, for now, only the pin number (first value of the parent
# specifier field) should be returned.
parent_pin_number = entry[child_specifier_size+1]
# Return gpio_controller and specifier pin
return phandles[parent_controller_phandle], parent_pin_number
# gpio_index did not match any entry in the gpio-map
return None, None
def extract_controller(node_path, prop, prop_values, index,
def_label, generic, handle_single=False,
deprecate=False):
prop_def = {}
prop_alias = {}
prop_array = build_cell_array(prop_values)
if handle_single:
prop_array = [prop_array[index]]
for i, elem in enumerate(prop_array):
num_cells = len(elem)
# if the entry is empty, skip
if num_cells == 0:
continue
cell_parent = phandles[elem[0]]
if 'gpio-map' in reduced[cell_parent]['props']:
# Parent is a gpio 'nexus node' (ie has gpio-map).
# Controller should be found in the map, using elem[1] as index.
# Pin attribues (number, flag) will not be used in this function
cell_parent, _ = child_to_parent_unmap(cell_parent, elem[1])
if cell_parent is None:
raise Exception("No parent matching child specifier")
l_cell = reduced[cell_parent]['props'].get('label')
if l_cell is None:
continue
l_base = [def_label]
# Check is defined should be indexed (_0, _1)
if handle_single or i == 0 and len(prop_array) == 1:
# 0 or 1 element in prop_values
l_idx = []
else:
l_idx = [str(i)]
l_cellname = str_to_label(generic + '_' + 'controller')
label = l_base + [l_cellname] + l_idx
add_compat_alias(node_path, '_'.join(label[1:]), '_'.join(label), prop_alias, deprecate)
prop_def['_'.join(label)] = "\"" + l_cell + "\""
#generate defs also if node is referenced as an alias in dts
if node_path in aliases:
add_prop_aliases(
node_path,
lambda alias: '_'.join([str_to_label(alias)] + label[1:]),
'_'.join(label),
prop_alias, deprecate)
insert_defs(node_path, prop_def, prop_alias)
if deprecate:
deprecated_main.extend(list(prop_def.keys()))
def extract_cells(node_path, prop, prop_values, names, index,
def_label, generic, handle_single=False,
deprecate=False):
prop_array = build_cell_array(prop_values)
if handle_single:
prop_array = [prop_array[index]]
for i, elem in enumerate(prop_array):
num_cells = len(elem)
# if the entry is empty, skip
if num_cells == 0:
continue
cell_parent = phandles[elem[0]]
if 'gpio-map' in reduced[cell_parent]['props']:
# Parent is a gpio connector ie 'nexus node', ie has gpio-map).
# Controller and pin number should be found in the connector map,
# using elem[1] as index.
# Parent pin flag is not used, so child flag(s) value (elem[2:])
# is kept as is.
cell_parent, elem[1] = child_to_parent_unmap(cell_parent, elem[1])
if cell_parent is None:
raise Exception("No parent matching child specifier")
try:
cell_yaml = get_binding(cell_parent)
except:
raise Exception(
"Could not find yaml description for " +
reduced[cell_parent]['name'])
try:
name = names.pop(0).upper()
except Exception:
name = ''
# Get number of cells per element of current property
for props in reduced[cell_parent]['props']:
if props[0] == '#' and '-cells' in props:
if props in cell_yaml:
cell_yaml_names = props
else:
cell_yaml_names = '#cells'
l_cell = [str_to_label(str(generic))]
l_base = [def_label]
# Check if #define should be indexed (_0, _1, ...)
if handle_single or i == 0 and len(prop_array) == 1:
# Less than 2 elements in prop_values
# Indexing is not needed
l_idx = []
else:
l_idx = [str(i)]
prop_def = {}
prop_alias = {}
# Generate label for each field of the property element
for j in range(num_cells-1):
l_cellname = [str(cell_yaml[cell_yaml_names][j]).upper()]
if l_cell == l_cellname:
label = l_base + l_cell + l_idx
else:
label = l_base + l_cell + l_cellname + l_idx
label_name = l_base + [name] + l_cellname
add_compat_alias(node_path, '_'.join(label[1:]), '_'.join(label), prop_alias, deprecate)
prop_def['_'.join(label)] = elem[j+1]
if name:
prop_alias['_'.join(label_name)] = '_'.join(label)
# generate defs for node aliases
if node_path in aliases:
add_prop_aliases(
node_path,
lambda alias: '_'.join([str_to_label(alias)] + label[1:]),
'_'.join(label),
prop_alias, deprecate)
insert_defs(node_path, prop_def, prop_alias)
if deprecate:
deprecated_main.extend(list(prop_def.keys()))
def err(msg):
# General error reporting helper. Prints a message to stderr and exits with
# status 1.
sys.exit("error: " + msg)