From 0dd210c9567b04faa17c2e46fe84a5d96a53f798 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 2 Nov 2018 17:26:41 +0100 Subject: [PATCH 01/13] scripts: cogeno: add inline code generation Cogeno is a file generation tool. It uses pieces of Python or Jinja2 script code in the source file as generators to generate whatever text is needed. Cogeno is based on Cog. All files taken from Cog are modified. The files are marked by the Cog author's copyright: Copyright 2004-2016, Ned Batchelder. http://nedbatchelder.com/code/cog SPDX-License-Identifier: MIT Origin: https://pypi.python.org/pypi/cogapp Status: 2.5.1 Description: A code generator for executing Python snippets in source files. Dependencies: python URL: https://pypi.python.org/pypi/cogapp commit: 2.5.1 Maintained-by: External License: MIT License Link: URL: https://pypi.python.org/pypi/cogapp Signed-off-by: Bobby Noelte --- scripts/cogeno/.gitignore | 116 +++++++ scripts/cogeno/cogeno/__init__.py | 7 + scripts/cogeno/cogeno/cmake.py | 190 +++++++++++ scripts/cogeno/cogeno/cogeno.py | 62 ++++ scripts/cogeno/cogeno/config.py | 68 ++++ scripts/cogeno/cogeno/context.py | 343 ++++++++++++++++++++ scripts/cogeno/cogeno/edts.py | 115 +++++++ scripts/cogeno/cogeno/error.py | 112 +++++++ scripts/cogeno/cogeno/filelock.py | 434 ++++++++++++++++++++++++++ scripts/cogeno/cogeno/filereader.py | 20 ++ scripts/cogeno/cogeno/generator.py | 237 ++++++++++++++ scripts/cogeno/cogeno/generic.py | 51 +++ scripts/cogeno/cogeno/guard.py | 16 + scripts/cogeno/cogeno/importmodule.py | 33 ++ scripts/cogeno/cogeno/include.py | 57 ++++ scripts/cogeno/cogeno/inlinegen.py | 342 ++++++++++++++++++++ scripts/cogeno/cogeno/jinja2gen.py | 169 ++++++++++ scripts/cogeno/cogeno/lock.py | 34 ++ scripts/cogeno/cogeno/log.py | 70 +++++ scripts/cogeno/cogeno/options.py | 260 +++++++++++++++ scripts/cogeno/cogeno/output.py | 34 ++ scripts/cogeno/cogeno/paths.py | 81 +++++ scripts/cogeno/cogeno/pygen.py | 81 +++++ scripts/cogeno/cogeno/redirectable.py | 31 ++ scripts/cogeno/cogeno/whiteutils.py | 72 +++++ 25 files changed, 3035 insertions(+) create mode 100644 scripts/cogeno/.gitignore create mode 100644 scripts/cogeno/cogeno/__init__.py create mode 100644 scripts/cogeno/cogeno/cmake.py create mode 100644 scripts/cogeno/cogeno/cogeno.py create mode 100644 scripts/cogeno/cogeno/config.py create mode 100644 scripts/cogeno/cogeno/context.py create mode 100644 scripts/cogeno/cogeno/edts.py create mode 100644 scripts/cogeno/cogeno/error.py create mode 100644 scripts/cogeno/cogeno/filelock.py create mode 100644 scripts/cogeno/cogeno/filereader.py create mode 100644 scripts/cogeno/cogeno/generator.py create mode 100644 scripts/cogeno/cogeno/generic.py create mode 100644 scripts/cogeno/cogeno/guard.py create mode 100644 scripts/cogeno/cogeno/importmodule.py create mode 100644 scripts/cogeno/cogeno/include.py create mode 100644 scripts/cogeno/cogeno/inlinegen.py create mode 100644 scripts/cogeno/cogeno/jinja2gen.py create mode 100644 scripts/cogeno/cogeno/lock.py create mode 100644 scripts/cogeno/cogeno/log.py create mode 100644 scripts/cogeno/cogeno/options.py create mode 100644 scripts/cogeno/cogeno/output.py create mode 100644 scripts/cogeno/cogeno/paths.py create mode 100644 scripts/cogeno/cogeno/pygen.py create mode 100644 scripts/cogeno/cogeno/redirectable.py create mode 100644 scripts/cogeno/cogeno/whiteutils.py diff --git a/scripts/cogeno/.gitignore b/scripts/cogeno/.gitignore new file mode 100644 index 000000000000..ecea41ab4849 --- /dev/null +++ b/scripts/cogeno/.gitignore @@ -0,0 +1,116 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +_build/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/scripts/cogeno/cogeno/__init__.py b/scripts/cogeno/cogeno/__init__.py new file mode 100644 index 000000000000..55a39fab19ef --- /dev/null +++ b/scripts/cogeno/cogeno/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) 2017 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Empty to allow all modules to be imported diff --git a/scripts/cogeno/cogeno/cmake.py b/scripts/cogeno/cogeno/cmake.py new file mode 100644 index 000000000000..319e0880a59f --- /dev/null +++ b/scripts/cogeno/cogeno/cmake.py @@ -0,0 +1,190 @@ +# Copyright (c) 2018 Open Source Foundries Limited. +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 +# +# CMakeCacheEntry and CMakeCache are taken from scripts/zephyr_run.py. +# + +import os +import sys +import re +from collections import OrderedDict +from pathlib import Path + + +class CMakeCacheEntry: + '''Represents a CMake cache entry. + This class understands the type system in a CMakeCache.txt, and + converts the following cache types to Python types: + Cache Type Python type + ---------- ------------------------------------------- + FILEPATH str + PATH str + STRING str OR list of str (if ';' is in the value) + BOOL bool + INTERNAL str OR list of str (if ';' is in the value) + ---------- ------------------------------------------- + ''' + + # Regular expression for a cache entry. + # + # CMake variable names can include escape characters, allowing a + # wider set of names than is easy to match with a regular + # expression. To be permissive here, use a non-greedy match up to + # the first colon (':'). This breaks if the variable name has a + # colon inside, but it's good enough. + CACHE_ENTRY = re.compile( + r'''(?P.*?) # name + :(?PFILEPATH|PATH|STRING|BOOL|INTERNAL) # type + =(?P.*) # value + ''', re.X) + + @classmethod + def _to_bool(cls, val): + # Convert a CMake BOOL string into a Python bool. + # + # "True if the constant is 1, ON, YES, TRUE, Y, or a + # non-zero number. False if the constant is 0, OFF, NO, + # FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in + # the suffix -NOTFOUND. Named boolean constants are + # case-insensitive. If the argument is not one of these + # constants, it is treated as a variable." + # + # https://cmake.org/cmake/help/v3.0/command/if.html + val = val.upper() + if val in ('ON', 'YES', 'TRUE', 'Y'): + return True + elif val in ('OFF', 'NO', 'FALSE', 'N', 'IGNORE', 'NOTFOUND', ''): + return False + elif val.endswith('-NOTFOUND'): + return False + else: + try: + v = int(val) + return v != 0 + except ValueError as exc: + raise ValueError('invalid bool {}'.format(val)) from exc + + @classmethod + def from_line(cls, line, line_no): + # Comments can only occur at the beginning of a line. + # (The value of an entry could contain a comment character). + if line.startswith('//') or line.startswith('#'): + return None + + # Whitespace-only lines do not contain cache entries. + if not line.strip(): + return None + + m = cls.CACHE_ENTRY.match(line) + if not m: + return None + + name, type_, value = (m.group(g) for g in ('name', 'type', 'value')) + if type_ == 'BOOL': + try: + value = cls._to_bool(value) + except ValueError as exc: + args = exc.args + ('on line {}: {}'.format(line_no, line),) + raise ValueError(args) from exc + elif type_ == 'STRING' or type_ == 'INTERNAL': + # If the value is a CMake list (i.e. is a string which + # contains a ';'), convert to a Python list. + if ';' in value: + value = value.split(';') + + return CMakeCacheEntry(name, value) + + def __init__(self, name, value): + self.name = name + self.value = value + + def __str__(self): + fmt = 'CMakeCacheEntry(name={}, value={})' + return fmt.format(self.name, self.value) + + +class CMakeCache: + '''Parses and represents a CMake cache file.''' + + def __init__(self, cache_file): + self.load(cache_file) + + def load(self, cache_file): + entries = [] + with open(str(cache_file), 'r') as cache: + for line_no, line in enumerate(cache): + entry = CMakeCacheEntry.from_line(line, line_no) + if entry: + entries.append(entry) + self._entries = OrderedDict((e.name, e) for e in entries) + + def get(self, name, default=None): + entry = self._entries.get(name) + if entry is not None: + return entry.value + else: + return default + + def get_list(self, name, default=None): + if default is None: + default = [] + entry = self._entries.get(name) + if entry is not None: + value = entry.value + if isinstance(value, list): + return value + elif isinstance(value, str): + return [value] + else: + msg = 'invalid value {} type {}' + raise RuntimeError(msg.format(value, type(value))) + else: + return default + + def __getitem__(self, name): + return self._entries[name].value + + def __setitem__(self, name, entry): + if not isinstance(entry, CMakeCacheEntry): + msg = 'improper type {} for value {}, expecting CMakeCacheEntry' + raise TypeError(msg.format(type(entry), entry)) + self._entries[name] = entry + + def __delitem__(self, name): + del self._entries[name] + + def __iter__(self): + return iter(self._entries.values()) + + +class CMakeMixin(object): + __slots__ = [] + + _cmake_cache = None + + def cmake_variable(self, variable_name, default=""): + variable_value = self.options.defines.get(variable_name, default) + if variable_value == "": + raise self._get_error_exception( + "CMake variable '{}' not defined.".format(variable_name), 1) + return variable_value + + def cmake_cache_variable(self, variable_name, default=""): + if self._cmake_cache is None: + cache_file = Path(self.options.cmakecache_file) + if not cache_file.is_file(): + raise self._get_error_exception( + "CMake cache file '{}' does not exist or is no file.". + format(cache_file), 1) + self._cmake_cache = CMakeCache(cache_file) + try: + return self._cmake_cache.get(variable_name) + except: + if default == "": + raise self._get_error_exception( + "CMake variable '{}' not defined in cache file.". + format(variable_name), 1) + return default + diff --git a/scripts/cogeno/cogeno/cogeno.py b/scripts/cogeno/cogeno/cogeno.py new file mode 100644 index 000000000000..d06d66becd81 --- /dev/null +++ b/scripts/cogeno/cogeno/cogeno.py @@ -0,0 +1,62 @@ +# +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: MIT + +import sys + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from generator import CodeGenerator + from redirectable import Redirectable + from options import Options + from context import Context +else: + # use current package visibility + from .generator import CodeGenerator + from .redirectable import Redirectable + from .options import Options + from .context import Context + +## +# @brief The code generation processor +# +class Cogeno(Redirectable): + + def __init__(self): + Redirectable.__init__(self) + + ## + # @brief Is this a trailing line after an end spec marker. + # + # @todo Make trailing end spec line detection dependent on + # type of text or file type. + # + # @param s line + # + def _is_end_spec_trailer(self, s): + return '*/' in s + + def callable_main(self, argv): + """ All of command-line cogen, but in a callable form. + This is used by main. + argv is the equivalent of sys.argv. + """ + options = Options() + options.parse_args(argv[1:]) + + generator = CodeGenerator() + context = Context(generator, options = options) + + ret = generator._evaluate_context(context) + return ret + + +def main(): + ret = Cogeno().callable_main(sys.argv) + sys.exit(ret) + +if __name__ == '__main__': + main() diff --git a/scripts/cogeno/cogeno/config.py b/scripts/cogeno/cogeno/config.py new file mode 100644 index 000000000000..0169337fa3d2 --- /dev/null +++ b/scripts/cogeno/cogeno/config.py @@ -0,0 +1,68 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys +import shlex +from pathlib import Path + +class ConfigMixin(object): + __slots__ = [] + + _autoconf = None + _autoconf_filename = None + + def _autoconf_assure(self): + if self._autoconf is None: + autoconf_file = self._context._options.config_file + if autoconf_file is None: + raise self._get_error_exception( + "No path defined for the config file.", 2) + autoconf_file = Path(autoconf_file) + if not autoconf_file.is_file(): + raise self._get_error_exception( + "Config file {} not found/ no access.". + format(autoconf_file), 2) + + self.log('s{}: access config {}' + .format(len(self.cogeno_module_states), + str(autoconf_file))) + + autoconf = {} + with autoconf_file.open(mode = 'r', encoding = 'utf-8') as autoconf_fd: + for line in autoconf_fd: + if line.startswith('#') or not line.strip(): + continue + config = line.split('=') + key = config[0].strip() + value = config[1].strip() + if value in ('y'): + value = "true" + elif value in ('n'): + value = "false" + else: + value = value.replace('"','') + autoconf[key] = value + + self._autoconf = autoconf + self._autoconf_filename = str(autoconf_file) + + def config_property(self, property_name, default=""): + self._autoconf_assure() + property_value = self._autoconf.get(property_name, default) + if property_value == "": + raise self._get_error_exception( + "Config property '{}' not defined.".format(property_name), 1) + return property_value + + ## + # @brief Get all config properties. + # + # The property names are the ones autoconf.conf. + # + # @return A dictionary of config properties. + # + def config_properties(self): + self._autoconf_assure() + return self._autoconf diff --git a/scripts/cogeno/cogeno/context.py b/scripts/cogeno/cogeno/context.py new file mode 100644 index 000000000000..521b40ed1ff9 --- /dev/null +++ b/scripts/cogeno/cogeno/context.py @@ -0,0 +1,343 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + + +from pathlib import Path + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from options import Options +else: + # use current package visibility + from .options import Options + +## +# @brief Context for code generation +# +class Context(object): + + _suffix_replacements = { + ".h.in" : ".h", + ".h.py" : ".h", + ".h.cogeno" : ".h", + ".h.jinja2" : ".h", + ".c.in" : ".c", + ".c.py" : ".c", + ".c.cogeno" : ".c", + ".c.jinja2" : ".c", + ".html.in" : ".html", + ".html.py" : ".html", + ".html.cogeno" : ".html", + ".html.jinja2" : ".html", + } + + # + # @brief Get template file from options + # + def _options_template_file(self): + if self._options.input_file is None: + # No input file given + self.error("No input file given (expected cogeno template).\n" + + "'{}'.".format(self._options), + frame_index = -2) + input_file = self._options.input_file + try: + template_file = Path(input_file).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + template_file = Path(input_file) + if not template_file.is_file(): + # Input does not exist + self.error("Input file '{}' does not exist." + .format(input_file), + frame_index = -2) + return str(template_file) + + # + # @brief Get output file from options + # + def _options_output_file(self): + if self._options.sOutputName is None: + return '-' + if self._options.sOutputName == '-': + return self._options.sOutputName + output_name = self._options.sOutputName + try: + output_file = Path(output_name).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + output_file = Path(output_name) + if not output_file.parent.is_dir(): + # directory does not exist + self.error("Output directory '{}' does not exist." + .format(output_file.parent), + frame_index = -2) + # Check for some common output endings that stem from + # input file name copy + output_file = str(output_file) + for suffix in self._suffix_replacements.keys(): + if output_file.endswith(suffix) \ + and self._template_file.endswith(suffix): + output_file = output_file.replace(suffix, + self._suffix_replacements[suffix]) + break + return output_file + + # + # @brief Get log file from options + # + def _options_log_file(self): + if self._options.log_file is None: + return '-' + if self._options.log_file == '-': + return self._options.log_file + log_name = self._options.log_file + try: + log_file = Path(log_name).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + log_file = Path(log_name) + if not log_file.parent.is_dir(): + # directory does not exist + self.error("Log directory '{}' does not exist." + .format(log_file.parent), + frame_index = -2) + log_file = str(log_file) + return log_file + + # + # @brief Get lock file from options + # + def _options_lock_file(self): + if self._options.lock_file is None: + return None + lock_name = self._options.lock_file + try: + lock_file = Path(lock_name).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + lock_file = Path(lock_name) + if not lock_file.parent.is_dir(): + # directory does not exist + self.error("Lock directory '{}' does not exist." + .format(lock_file.parent), + frame_index = -2) + lock_file = str(lock_file) + return lock_file + + def __init__(self, + generator, + parent_context = None, + generation_globals = None, + options = None, + eval_begin = None, + eval_end = None, + eval_adjust = None, + delete_code = None, + template_file = None, + template = None, + template_source_type = None, + script_type = None, + template_tabsize = None, + templates_paths = None, + modules_paths = None, + jinja2_environment = None, + output_file = None, + log_file = None, + lock_file = None): + # The code generator that will use this context + self._generator = generator + # Only the top level template does not have a parent + self._parent = parent_context + # Code generation usually works on the same global namespace + self._globals = generation_globals + # Options usually from the top level template + self._options = options + # The output of this context + self._outstring = '' + # The path/name of the output file + self._output_file = output_file + # The path/name of the log file + self._log_file = log_file + # The path/name of the lock file (concurrent processes of cogeno) + self._lock_file = lock_file + # the current evaluation begin and end offset in the template + self._eval_begin = eval_begin + self._eval_end = eval_end + # value to adjust line numbers reported by script compilation + # and execution + self._eval_adjust = eval_adjust + # Delete the generator code from the output + self._delete_code = delete_code + # The path of the template file or name of template + self._template_file = template_file + # The template this context works on + self._template = template + # The template source type ('snippet', "file", 'string', None) + self._template_source_type = template_source_type + # The template script type ('inline', "python", 'jinja2', None) + self._script_type = script_type + # The tabsize in thew template code + self._template_tabsize = template_tabsize + # Paths to search template files + self._templates_paths = [] + # Paths to search Python modules + self._modules_paths = [] + # Jinja2 environment for Jinja2 script execution + self._jinja2_environment = jinja2_environment + + # init unknown generation _globals to parent scope if possible + if self._globals is None: + if self._parent is None: + self._globals = {} + # list of include files that are guarded against inclusion + self._globals['_guard_include'] = [] + # global flag for code generation + self._globals['_generate_code'] = True + else: + self._globals = self._parent._globals + # init unknown options to parent scope if possible + if self._options is None: + if self._parent is None: + # use default options + self._options = Options() + else: + self._options = self._parent._options + # -- + # from now on we can use the options to read values that are not given + # -- + + # init unknown template + if self._template_file is None: + if self._parent is None: + self._template_file = self._options_template_file() + self._template_source_type = 'file' + else: + self._parent._template_file + self._template_source_type = self._parent._template_source_type + # init unknown output file to parent scope if possible + if self._output_file is None: + if self._parent is None: + self._output_file = self._options_output_file() + else: + self._output_file = self._parent._output_file + # init unknown lock file to parent scope if possible + if self._log_file is None: + if self._parent is None: + self._log_file = self._options_log_file() + else: + self._log_file = self._parent._log_file + # init unknown lock file to parent scope if possible + if self._lock_file is None: + if self._parent is None: + self._lock_file = self._options_lock_file() + else: + self._lock_file = self._parent._lock_file + # init unknown eval_adjust value + if self._eval_adjust is None: + self._eval_adjust = 0 + # init unknown delete code value + if self._delete_code is None: + if self._parent is None: + # use default options + self._delete_code = self._options.delete_code + else: + self._delete_code = self._parent._delete_code + # init unknown tabsize to parent scope if possible + if self._template_tabsize is None: + if self._parent is None: + # use default options + self._template_tabsize = 8 + else: + self._template_tabsize = self._parent._template_tabsize + # init modules paths + if modules_paths is None: + if self._parent is None: + modules_paths = self._options.modules_paths + else: + modules_paths = self._parent._modules_paths + if modules_paths: + if not isinstance(modules_paths, list): + modules_paths = [modules_paths,] + for path in modules_paths: + self._modules_paths.append(path) + self._modules_paths.append(Path(generator.cogeno_path(), 'modules')) + # init templates paths + if templates_paths is None: + if self._parent is None: + templates_paths = self._options.templates_paths + else: + templates_paths = self._parent._templates_paths + if templates_paths: + if not isinstance(templates_paths, list): + templates_paths = [templates_paths,] + for path in templates_paths: + self._templates_paths.append(path) + self._templates_paths.append(Path(generator.cogeno_path(), 'templates')) + # init jinja2 environment + # Jinja2 environment will only be created if there is a Jinja2 use. + if self._jinja2_environment is None: + if not self._parent is None: + self._jinja2_environment = self._parent._jinja2_environment + + def __str__(self): + sb = [] + for key in self.__dict__: + sb.append("{key}='{value}'".format(key=key, value=self.__dict__[key])) + return ', '.join(sb) + + def __repr__(self): + return self.__str__() + + def parent(self): + return self._parent + + def generation_globals(self): + return self._globals + + def script_is_inline(self): + return self._script_type == 'inline' + + def script_is_python(self): + return self._script_type == 'python' + + def script_is_jinja2(self): + return self._script_type == 'jinja2' + + def script_type(self): + return self._script_type + + ## + # @brief Template is a snippet. + # + # Snippets are parts of the template of + # the parent context. + # + # @return True in case the template is a snippet, + # False otherwise. + def template_is_snippet(self): + return self._template_source_type == 'snippet' + + def template_is_file(self): + return self._template_source_type == 'file' + + def template_is_string(self): + return self._template_source_type == 'string' + + ## + # @brief Add line + def out(self, line): + self._outstring += line + + ## + # @brief Add line with newline + def outl(self, line): + self._outstring += line + '\n' + diff --git a/scripts/cogeno/cogeno/edts.py b/scripts/cogeno/cogeno/edts.py new file mode 100644 index 000000000000..4234e1b70360 --- /dev/null +++ b/scripts/cogeno/cogeno/edts.py @@ -0,0 +1,115 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import time +from pathlib import Path + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from modules.edtsdatabase import EDTSDatabase +else: + # use current package visibility + from .modules.edtsdatabase import EDTSDatabase + +class EDTSMixin(object): + __slots__ = [] + + _edts = None + + def _edts_assure(self): + if self._edts is not None: + return + + # EDTS database file + edts_file = self._context._options.edts_file + if edts_file is None: + raise self._get_error_exception( + "No path defined for the extended device tree database file.", 2) + edts_file = Path(edts_file) + # DTS compiled file + dts_file = self._context._options.dts_file + if dts_file is None: + raise self._get_error_exception( + "No path defined for the device tree specification file.", 2) + dts_file = Path(dts_file) + if not dts_file.is_file() and not edts_file.is_file(): + raise self._get_error_exception( + "Device tree specification file '{}' not found/ no access.". + format(dts_file), 2) + # Bindings + bindings_paths = self._context._options.bindings_paths + if len(bindings_paths) == 0 and not edts_file.is_file(): + raise self._get_error_exception( + "No path defined for the device tree bindings.", 2) + + # Check whether extraction is necessary + if edts_file.is_file(): + # EDTS file must be newer than the DTS compiler file + dts_date = dts_file.stat().st_mtime + edts_date = edts_file.stat().st_mtime + if dts_date > edts_date: + extract = True + else: + extract = False + else: + extract = True + + self.log('s{}: access EDTS {} with lock {}' + .format(len(self.cogeno_module_states), + str(edts_file), self._context._lock_file)) + + # Try to get a lock for the database file + # If we do not get the log for 10 seconds an + # exception is thrown. + try: + with self.lock().acquire(timeout = 10): + if extract: + self.log('s{}: extract EDTS {} from {} with bindings {}' + .format(len(self.cogeno_module_states), + str(edts_file), + str(dts_file), + bindings_paths)) + if edts_file.is_file(): + # Remove old file + edts_file.unlink() + unlink_wait_count = 0 + while edts_file.is_file(): + # do dummy access to wait for unlink + time.sleep(1) + unlink_wait_count += 1 + if unlink_wait_count > 5: + self.error( + "Generated extended device tree database file '{}' no unlink." + .format(edts_file), frame_index = 2) + # Create EDTS database by extraction + self._edts = EDTSDatabase() + self._edts.extract(dts_file, bindings_paths) + # Store file to be reused + self._edts.save(edts_file) + else: + if not edts_file.is_file(): + self.error( + "Generated extended device tree database file '{}' not found/ no access." + .format(edts_file), frame_index = 2) + self._edts = EDTSDatabase() + self._edts.load(edts_file) + + except self.LockTimeout: + # Something went really wrong - we did not get the lock + self.error( + "Generated extended device tree database file '{}' no access." + .format(edts_file), frame_index = 2) + except: + raise + + ## + # @brief Get the extended device tree database. + # + # @return Extended device tree database. + # + def edts(self): + self._edts_assure() + return self._edts diff --git a/scripts/cogeno/cogeno/error.py b/scripts/cogeno/cogeno/error.py new file mode 100644 index 000000000000..69d7b35b2ba3 --- /dev/null +++ b/scripts/cogeno/cogeno/error.py @@ -0,0 +1,112 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import inspect +from pathlib import Path + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from context import Context +else: + # use current package visibility + from .context import Context + +class Error(Exception): + pass + +class ErrorMixin(object): + __slots__ = [] + + ## + # @brief list snippet + def _list_snippet(self): + template = self._context.parent()._template + if not template: + return None + listing = "" + snippet_lineno = 0 - self._context._eval_adjust + template_file = self._context._template_file + eval_begin = self._context.parent()._eval_begin + eval_end = self._context.parent()._eval_end + for i, line in enumerate(template.splitlines()): + if i < eval_begin: + continue + if i >= eval_end: + break + if snippet_lineno < 0: + snippet_lineno += 1 + continue + if snippet_lineno >= 0: + listing += "\n" + listing += "{} line {} = #{}: {}".format( + template_file, + snippet_lineno - self._context._eval_adjust, + i - self._context._eval_adjust, + line) + snippet_lineno += 1 + return listing + + ## + # @brief Get code generation error exception + # + # @note only for 'raise cogen.Error(msg)' in template + # + # @param msg exception message + # @param frame_index [optional] call frame index + # @param lineno [optional] line number within template + # @return code generation exception object + # + def _get_error_exception(self, msg, frame_index = 0, + lineno = 0): + + if self._context.script_is_python(): + if frame_index >= 0: + # There are frames to get data from + frame_index += 1 + frame = inspect.currentframe() + try: + while frame_index > 0: + frame = frame.f_back + frame_index -= 1 + (filename, lineno, function, code_context, index) = \ + inspect.getframeinfo(frame) + except: + pass + finally: + del frame + if self._context.template_is_snippet(): + lineno = int(lineno) + template_lineno = self._context.parent()._eval_begin \ + + lineno + self._context._eval_adjust + error_msg = "{} line {} = #{}: {}".format( + self._context._template_file, lineno, + template_lineno, msg) + listing = self._list_snippet() + if listing: + error_msg = listing + '\n' + error_msg + else: + error_msg = msg + + else: + error_msg = msg + + return Error(error_msg) + + ## + # @brief Raise Error exception. + # + # Extra information is added that maps the python snippet + # line seen by the Python interpreter to the line of the file + # that inlines the python snippet. + # + # @param msg [optional] exception message + # @param frame_index [optional] call frame index + # @param lineno [optional] line number within template + # + def error(self, msg = 'Error raised by cogen generator.', + frame_index = 0, lineno = 0): + frame_index += 1 + raise self._get_error_exception(msg, frame_index, lineno) diff --git a/scripts/cogeno/cogeno/filelock.py b/scripts/cogeno/cogeno/filelock.py new file mode 100644 index 000000000000..af65b078f263 --- /dev/null +++ b/scripts/cogeno/cogeno/filelock.py @@ -0,0 +1,434 @@ +# Copyright (c) 2018 Benedikt Schmitt. +# +# SPDX-License-Identifier: Unlicense + +# +# Copied from https://github.com/benediktschmitt/py-filelock +# + +""" +A platform independent file lock that supports the with-statement. +""" + + +# Modules +# ------------------------------------------------ +import logging +import os +import threading +import time +try: + import warnings +except ImportError: + warnings = None + +try: + import msvcrt +except ImportError: + msvcrt = None + +try: + import fcntl +except ImportError: + fcntl = None + + +# Backward compatibility +# ------------------------------------------------ +try: + TimeoutError +except NameError: + TimeoutError = OSError + + +# Data +# ------------------------------------------------ +__all__ = [ + "Timeout", + "BaseFileLock", + "WindowsFileLock", + "UnixFileLock", + "SoftFileLock", + "FileLock" +] + +__version__ = "3.0.9" + + +_logger = None +def logger(): + """Returns the logger instance used in this module.""" + global _logger + _logger = _logger or logging.getLogger(__name__) + return _logger + + +# Exceptions +# ------------------------------------------------ +class Timeout(TimeoutError): + """ + Raised when the lock could not be acquired in *timeout* + seconds. + """ + + def __init__(self, lock_file): + """ + """ + #: The path of the file lock. + self.lock_file = lock_file + return None + + def __str__(self): + temp = "The file lock '{}' could not be acquired."\ + .format(self.lock_file) + return temp + + +# Classes +# ------------------------------------------------ + +# This is a helper class which is returned by :meth:`BaseFileLock.acquire` +# and wraps the lock to make sure __enter__ is not called twice when entering +# the with statement. +# If we would simply return *self*, the lock would be acquired again +# in the *__enter__* method of the BaseFileLock, but not released again +# automatically. +# +# :seealso: issue #37 (memory leak) +class _Acquire_ReturnProxy(object): + + def __init__(self, lock): + self.lock = lock + return None + + def __enter__(self): + return self.lock + + def __exit__(self, exc_type, exc_value, traceback): + self.lock.release() + return None + + +class BaseFileLock(object): + """ + Implements the base class of a file lock. + """ + + def __init__(self, lock_file, timeout = -1): + """ + """ + # The path to the lock file. + self._lock_file = lock_file + + # The file descriptor for the *_lock_file* as it is returned by the + # os.open() function. + # This file lock is only NOT None, if the object currently holds the + # lock. + self._lock_file_fd = None + + # The default timeout value. + self.timeout = timeout + + # We use this lock primarily for the lock counter. + self._thread_lock = threading.Lock() + + # The lock counter is used for implementing the nested locking + # mechanism. Whenever the lock is acquired, the counter is increased and + # the lock is only released, when this value is 0 again. + self._lock_counter = 0 + return None + + @property + def lock_file(self): + """ + The path to the lock file. + """ + return self._lock_file + + @property + def timeout(self): + """ + You can set a default timeout for the filelock. It will be used as + fallback value in the acquire method, if no timeout value (*None*) is + given. + + If you want to disable the timeout, set it to a negative value. + + A timeout of 0 means, that there is exactly one attempt to acquire the + file lock. + + .. versionadded:: 2.0.0 + """ + return self._timeout + + @timeout.setter + def timeout(self, value): + """ + """ + self._timeout = float(value) + return None + + # Platform dependent locking + # -------------------------------------------- + + def _acquire(self): + """ + Platform dependent. If the file lock could be + acquired, self._lock_file_fd holds the file descriptor + of the lock file. + """ + raise NotImplementedError() + + def _release(self): + """ + Releases the lock and sets self._lock_file_fd to None. + """ + raise NotImplementedError() + + # Platform independent methods + # -------------------------------------------- + + @property + def is_locked(self): + """ + True, if the object holds the file lock. + + .. versionchanged:: 2.0.0 + + This was previously a method and is now a property. + """ + return self._lock_file_fd is not None + + def acquire(self, timeout=None, poll_intervall=0.05): + """ + Acquires the file lock or fails with a :exc:`Timeout` error. + + .. code-block:: python + + # You can use this method in the context manager (recommended) + with lock.acquire(): + pass + + # Or use an equivalent try-finally construct: + lock.acquire() + try: + pass + finally: + lock.release() + + :arg float timeout: + The maximum time waited for the file lock. + If ``timeout <= 0``, there is no timeout and this method will + block until the lock could be acquired. + If ``timeout`` is None, the default :attr:`~timeout` is used. + + :arg float poll_intervall: + We check once in *poll_intervall* seconds if we can acquire the + file lock. + + :raises Timeout: + if the lock could not be acquired in *timeout* seconds. + + .. versionchanged:: 2.0.0 + + This method returns now a *proxy* object instead of *self*, + so that it can be used in a with statement without side effects. + """ + # Use the default timeout, if no timeout is provided. + if timeout is None: + timeout = self.timeout + + # Increment the number right at the beginning. + # We can still undo it, if something fails. + with self._thread_lock: + self._lock_counter += 1 + + lock_id = id(self) + lock_filename = self._lock_file + start_time = time.time() + try: + while True: + with self._thread_lock: + if not self.is_locked: + logger().debug('Attempting to acquire lock %s on %s', lock_id, lock_filename) + self._acquire() + + if self.is_locked: + logger().info('Lock %s acquired on %s', lock_id, lock_filename) + break + elif timeout >= 0 and time.time() - start_time > timeout: + logger().debug('Timeout on acquiring lock %s on %s', lock_id, lock_filename) + raise Timeout(self._lock_file) + else: + logger().debug( + 'Lock %s not acquired on %s, waiting %s seconds ...', + lock_id, lock_filename, poll_intervall + ) + time.sleep(poll_intervall) + except: + # Something did go wrong, so decrement the counter. + with self._thread_lock: + self._lock_counter = max(0, self._lock_counter - 1) + + raise + return _Acquire_ReturnProxy(lock = self) + + def release(self, force = False): + """ + Releases the file lock. + + Please note, that the lock is only completely released, if the lock + counter is 0. + + Also note, that the lock file itself is not automatically deleted. + + :arg bool force: + If true, the lock counter is ignored and the lock is released in + every case. + """ + with self._thread_lock: + + if self.is_locked: + self._lock_counter -= 1 + + if self._lock_counter == 0 or force: + lock_id = id(self) + lock_filename = self._lock_file + + logger().debug('Attempting to release lock %s on %s', lock_id, lock_filename) + self._release() + self._lock_counter = 0 + logger().info('Lock %s released on %s', lock_id, lock_filename) + + return None + + def __enter__(self): + self.acquire() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.release() + return None + + def __del__(self): + self.release(force = True) + return None + + +# Windows locking mechanism +# ~~~~~~~~~~~~~~~~~~~~~~~~~ + +class WindowsFileLock(BaseFileLock): + """ + Uses the :func:`msvcrt.locking` function to hard lock the lock file on + windows systems. + """ + + def _acquire(self): + open_mode = os.O_RDWR | os.O_CREAT | os.O_TRUNC + + try: + fd = os.open(self._lock_file, open_mode) + except OSError: + pass + else: + try: + msvcrt.locking(fd, msvcrt.LK_NBLCK, 1) + except (IOError, OSError): + os.close(fd) + else: + self._lock_file_fd = fd + return None + + def _release(self): + fd = self._lock_file_fd + self._lock_file_fd = None + msvcrt.locking(fd, msvcrt.LK_UNLCK, 1) + os.close(fd) + + try: + os.remove(self._lock_file) + # Probably another instance of the application + # that acquired the file lock. + except OSError: + pass + return None + +# Unix locking mechanism +# ~~~~~~~~~~~~~~~~~~~~~~ + +class UnixFileLock(BaseFileLock): + """ + Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems. + """ + + def _acquire(self): + open_mode = os.O_RDWR | os.O_CREAT | os.O_TRUNC + fd = os.open(self._lock_file, open_mode) + + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except (IOError, OSError): + os.close(fd) + else: + self._lock_file_fd = fd + return None + + def _release(self): + # Do not remove the lockfile: + # + # https://github.com/benediktschmitt/py-filelock/issues/31 + # https://stackoverflow.com/questions/17708885/flock-removing-locked-file-without-race-condition + fd = self._lock_file_fd + self._lock_file_fd = None + fcntl.flock(fd, fcntl.LOCK_UN) + os.close(fd) + return None + +# Soft lock +# ~~~~~~~~~ + +class SoftFileLock(BaseFileLock): + """ + Simply watches the existence of the lock file. + """ + + def _acquire(self): + open_mode = os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_TRUNC + try: + fd = os.open(self._lock_file, open_mode) + except (IOError, OSError): + pass + else: + self._lock_file_fd = fd + return None + + def _release(self): + os.close(self._lock_file_fd) + self._lock_file_fd = None + + try: + os.remove(self._lock_file) + # The file is already deleted and that's what we want. + except OSError: + pass + return None + + +# Platform filelock +# ~~~~~~~~~~~~~~~~~ + +#: Alias for the lock, which should be used for the current platform. On +#: Windows, this is an alias for :class:`WindowsFileLock`, on Unix for +#: :class:`UnixFileLock` and otherwise for :class:`SoftFileLock`. +FileLock = None + +if msvcrt: + FileLock = WindowsFileLock +elif fcntl: + FileLock = UnixFileLock +else: + FileLock = SoftFileLock + + if warnings is not None: + warnings.warn("only soft file lock is available") diff --git a/scripts/cogeno/cogeno/filereader.py b/scripts/cogeno/cogeno/filereader.py new file mode 100644 index 000000000000..c567a5ca24df --- /dev/null +++ b/scripts/cogeno/cogeno/filereader.py @@ -0,0 +1,20 @@ +# Copyright 2004-2016, Ned Batchelder. +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: MIT + +class NumberedFileReader: + """ A decorator for files that counts the readline()'s called. + """ + def __init__(self, f): + self.f = f + self.n = 0 + + def readline(self): + l = self.f.readline() + if l: + self.n += 1 + return l + + def linenumber(self): + return self.n diff --git a/scripts/cogeno/cogeno/generator.py b/scripts/cogeno/cogeno/generator.py new file mode 100644 index 000000000000..a422a6dce304 --- /dev/null +++ b/scripts/cogeno/cogeno/generator.py @@ -0,0 +1,237 @@ +# Copyright 2004-2016, Ned Batchelder. +# http://nedbatchelder.com/code/cog +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: MIT + +import sys +import os +import re +import imp +import inspect + +from pathlib import Path + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from whiteutils import * + from context import Context + from options import Options, OptionsMixin + from lock import LockMixin + from generic import GenericMixin + from inlinegen import InlineGenMixin + from pygen import PyGenMixin + from jinja2gen import Jinja2GenMixin + from guard import GuardMixin + from config import ConfigMixin + from cmake import CMakeMixin + from paths import PathsMixin + from edts import EDTSMixin + from include import IncludeMixin + from log import LogMixin + from error import ErrorMixin, Error + from output import OutputMixin + from importmodule import ImportMixin + from redirectable import RedirectableMixin +else: + # use current package visibility + from .whiteutils import * + from .context import Context + from .options import Options, OptionsMixin + from .lock import LockMixin + from .generic import GenericMixin + from .inlinegen import InlineGenMixin + from .pygen import PyGenMixin + from .jinja2gen import Jinja2GenMixin + from .guard import GuardMixin + from .config import ConfigMixin + from .cmake import CMakeMixin + from .paths import PathsMixin + from .edts import EDTSMixin + from .include import IncludeMixin + from .log import LogMixin + from .error import ErrorMixin, Error + from .output import OutputMixin + from .importmodule import ImportMixin + from .redirectable import RedirectableMixin + +class CodeGenerator(OptionsMixin, LockMixin, GenericMixin, InlineGenMixin, + PyGenMixin, Jinja2GenMixin, ConfigMixin, + CMakeMixin, PathsMixin, EDTSMixin, GuardMixin, + IncludeMixin, LogMixin, ErrorMixin, OutputMixin, + ImportMixin, RedirectableMixin): + + # The cogen module + cogeno_module = None + + # Stack of module module states + cogeno_module_states = [] + + ## + # @brief Magic mumbo-jumbo so that imported Python modules + # can say "import cogeno" and get our state. + # + # Make us the module. + @classmethod + def _init_cogeno_module(cls): + if cls.cogeno_module is None: + cls.cogeno_module = imp.new_module('cogeno') + cls.cogeno_module.path = [] + sys.modules['cogeno'] = cls.cogeno_module + + ## + # @brief Save our state to the "cogeno" module. + # + # Prepare to restore the current state before saving + # to the module + def _set_cogeno_module_state(self): + restore_state = {} + module_states = self.cogeno_module_states + module = self.cogeno_module + # Code generator state + restore_state['_context'] = getattr(module, '_context', None) + module._context = self._context + # Paths + restore_state['module_path'] = module.path[:] + restore_state['sys_path'] = sys.path[:] + # CodeGenerator methods that are relevant to templates + # Look for the Mixin classes. + for base_cls in inspect.getmro(CodeGenerator): + if "Mixin" in base_cls.__name__: + for member_name, member_value in inspect.getmembers(base_cls): + if member_name.startswith('_'): + continue + if inspect.isroutine(member_value): + restore_state[member_name] = \ + getattr(module, member_name, None) + setattr(module, member_name, + getattr(self, member_name)) + module_states.append(restore_state) + + def _restore_cogeno_module_state(self): + module_states = self.cogeno_module_states + module = self.cogeno_module + restore_state = module_states.pop() + # Paths + sys.path = restore_state['sys_path'] + module.path = restore_state['module_path'] + # Code generator state + module._context = restore_state['_context'] + # CodeGenerator methods that are relevant to templates + # Look for the Mixin classes. + for base_cls in inspect.getmro(CodeGenerator): + if "Mixin" in base_cls.__name__: + for member_name, member_value in inspect.getmembers(base_cls): + if member_name.startswith('_'): + continue + if inspect.isroutine(member_value): + setattr(module, member_name, restore_state[member_name]) + + + def __init__(self): + # the actual generation context + self._context = None + # Create the cogeno module if not available + self._init_cogeno_module() + + ## + # @brief evaluate context + # + # Inserts the context outstring in the current context + # + def _evaluate_context(self, context): + if context.parent() != self._context: + # This should never happen + self.error("Context '{}' with wrong parent '{}' for evaluation (expected '{}')." + .format(context, context.parent(), self._context), + frame_index = -2, lineno = 0) + + # make the new context the actual one + self._context = context + + if self._context.parent() is None: + # we are at toplevel context + # Assure the modules and templates paths + # from context are inserted to module + for path in self._context._modules_paths: + self.cogeno_module.path.extend(str(path)) + sys.path.extend(str(path)) + for path in self._context._templates_paths: + pass + # Assure the module does have our state + self._set_cogeno_module_state() + + if self._context.template_is_file(): + template_file = self.find_file_path(context._template_file, + self.templates_paths()) + if template_file is None: + self.error("File {} not found".format(context._template_file), + frame_index = -2, snippet_lineno = 0) + context._template_file = str(template_file) + if context.script_is_jinja2(): + # Jinja2 uses its own file loader + pass + else: + # Get whole file as a string + with template_file.open(mode = "r", encoding="utf-8") as template_fd: + context._template = template_fd.read() + + if self._context.script_type() is None: + # Do some heuristics to find out the template script type + # - a cogeno file with source code and inline code generation + # - a ninja template + # - a pure cogeno python template + if "@code{.cogeno" in context._template: + # we found a cogeno marker + self._context._script_type = "inline" + elif "{%" in context._template: + self._context._script_type = "jinja2" + elif "cogeno" in context._template: + self._context._script_type = "python" + elif context._template_file.endswith('.in'): + self._context._script_type = "inline" + elif context._template_file.endswith('.py'): + self._context._script_type = "python" + elif context._template_file.endswith('.jinja2'): + self._context._script_type = "jinja2" + else: + raise TypeError("File {} expected to be a cogeno template (template script type unknown)" + .format(context._template_file)) + + if self._context._eval_begin is None: + self._context._eval_begin = 0 + + if self._context.script_is_inline(): + self._inline_evaluate() + elif self._context.script_is_python(): + self._python_evaluate() + elif self._context.script_is_jinja2(): + self._jinja2_evaluate() + else: + # This should never happen + self.error("Context '{}' with unknown script type '{}' for evaluation." + .format(context, context.script_type()), + frame_index = -2) + + # switch back context + self._context = self._context.parent() + + if self._context is None: + # The context we evaluated is a top level context + if context._output_file == '-': + sys.stdout.write(context._outstring) + else: + with Path(context._output_file).open(mode = 'w', encoding = 'utf-8') as output_fd: + output_fd.write(context._outstring) + self.log('s{}: write {}' + .format(len(self.cogeno_module_states), context._output_file)) + self._restore_cogeno_module_state() + else: + self._context.out(context._outstring) + # Within Jinja2 context we have to return the string + # Just do it always + return context._outstring + + diff --git a/scripts/cogeno/cogeno/generic.py b/scripts/cogeno/cogeno/generic.py new file mode 100644 index 000000000000..85381ddddb8b --- /dev/null +++ b/scripts/cogeno/cogeno/generic.py @@ -0,0 +1,51 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +class GenericMixin(object): + __slots__ = [] + + @staticmethod + def path_walk(top, topdown = False, followlinks = False): + """ + See Python docs for os.walk, exact same behavior but it yields Path() + instances instead + + Form: http://ominian.com/2016/03/29/os-walk-for-pathlib-path/ + """ + names = list(top.iterdir()) + + dirs = (node for node in names if node.is_dir() is True) + nondirs = (node for node in names if node.is_dir() is False) + + if topdown: + yield top, dirs, nondirs + + for name in dirs: + if followlinks or name.is_symlink() is False: + for x in path_walk(name, topdown, followlinks): + yield x + + if topdown is not True: + yield top, dirs, nondirs + + # + # @param marker Marker as b'my-marker' + # + @staticmethod + def template_files(top, marker, suffix='.c'): + sources = [] + for path, directory_names, file_names in CoGen.path_walk(top): + sources.extend([x for x in file_names if x.suffix == suffix]) + + templates = [] + for source_file in sources: + if os.stat(source_file).st_size == 0: + continue + with open(source_file, 'rb', 0) as source_file_fd: + s = mmap.mmap(source_file_fd.fileno(), 0, access=mmap.ACCESS_READ) + if s.find(marker) != -1: + templates.append(source_file) + return templates diff --git a/scripts/cogeno/cogeno/guard.py b/scripts/cogeno/cogeno/guard.py new file mode 100644 index 000000000000..0cf8a11a22eb --- /dev/null +++ b/scripts/cogeno/cogeno/guard.py @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +class GuardMixin(object): + __slots__ = [] + + + def outl_guard_config(self, property_name): + is_config = self.config_property(property_name, 0) + self.outl("#if {} // Guard({}) {}".format( + is_config, is_config, property_name)) + + def outl_unguard_config(self, property_name): + is_config = self.config_property(property_name, 0) + self.outl("#endif // Guard({}) {}".format(is_config, property_name)) diff --git a/scripts/cogeno/cogeno/importmodule.py b/scripts/cogeno/cogeno/importmodule.py new file mode 100644 index 000000000000..1005b110ac91 --- /dev/null +++ b/scripts/cogeno/cogeno/importmodule.py @@ -0,0 +1,33 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import sys +import os +import importlib +from pathlib import Path + +class ImportMixin(object): + __slots__ = [] + + ## + # @brief Import a CoGen module. + # + # Import a module from the cogen/modules package. + # + # @param name Module to import. Specified without any path. + # + def import_module(self, name): + module_name = "{}.py".format(name) + module_file = self.find_file_path(module_name, self.modules_paths()) + if module_file is None: + raise self._get_error_exception( + "Module file '{}' of module '{}' does not exist or is no file.\n". + format(module_name, name) + \ + "Searched in {}.".format(self.modules_paths()), 1) + + sys.path.append(os.path.dirname(str(module_file))) + module = importlib.import_module(name) + sys.path.pop() + self._context._globals[name] = module + diff --git a/scripts/cogeno/cogeno/include.py b/scripts/cogeno/cogeno/include.py new file mode 100644 index 000000000000..5fddaed32825 --- /dev/null +++ b/scripts/cogeno/cogeno/include.py @@ -0,0 +1,57 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path +import io + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from error import Error + from context import Context +else: + # use current package visibility + from .error import Error + from .context import Context + +class IncludeMixin(object): + __slots__ = [] + + def out_include(self, include_file): + self.log('s{}: out_include {}' + .format(len(self.cogeno_module_states), include_file)) + input_file = self.find_file_path(include_file, self.templates_paths()) + if input_file is None: + raise self._get_error_exception( + "Include file '{}' does not exist or is no file.\n". + format(include_file) + "Searched in {}".format(self.templates_paths()), 1) + if str(input_file) in self._context._globals['_guard_include']: + self.log('------- include guarded {} - multiple inclusion of include file.'. + format(str(input_file))) + return '' + else: + include_context = Context(self, + parent_context = self._context, + template_file = str(input_file), + template_source_type = 'file', + delete_code = True, + ) + # delete inline code in included files + self.log('------- include start {}'.format(input_file)) + # let the devils work - evaluate context + # inserts the context output into the current context + self._evaluate_context(include_context) + self.log('------- include end {}'.format(input_file)) + return include_context._outstring + + def guard_include(self): + if self._context._template_file in self._context._globals['_guard_include']: + # This should never happen + raise self._get_error_exception( + "Multiple inclusion of guarded include file '{}'.". + format(self._context._template_file), 1) + self.log('------- include guard {}'.format(self._context._template_file)) + self._context._globals['_guard_include'].append(self._context._template_file) + diff --git a/scripts/cogeno/cogeno/inlinegen.py b/scripts/cogeno/cogeno/inlinegen.py new file mode 100644 index 000000000000..475c2bcd5586 --- /dev/null +++ b/scripts/cogeno/cogeno/inlinegen.py @@ -0,0 +1,342 @@ +# +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import re + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from error import Error + from context import Context +else: + # use current package visibility + from .error import Error + from .context import Context + +class InlineGenMixin(object): + __slots__ = [] + + _inline_python_start_marker = "@code{.cogeno.py}" + _inline_python_end_marker = "@endcode{.cogeno.py}" + _inline_jinja2_start_marker = "@code{.cogeno.jinja2}" + _inline_jinja2_end_marker = "@endcode{.cogeno.jinja2}" + _inline_insert_marker = "@code{.cogeno.ins}@endcode" + + # code snippet markers and lines + _inline_start_markers = (_inline_python_start_marker, + _inline_jinja2_start_marker) + _inline_end_markers = (_inline_python_end_marker, + _inline_jinja2_end_marker) + _inline_insert_markers = (_inline_insert_marker,) + + def _inline_start_marker(self, line): + for marker in self._inline_start_markers: + if marker in line: + return marker + return None + + def _inline_end_marker(self, line): + for marker in self._inline_end_markers: + if marker in line: + return marker + return None + + def _inline_insert_marker(self, line): + for marker in self._inline_insert_markers: + if marker in line: + return marker + return None + + def _inline_is_start_marker(self, line): + if self._inline_start_marker(line) is not None: + return True + return False + + def _inline_is_end_marker(self, line): + if self._inline_end_marker(line) is not None: + return True + return False + + def _inline_is_insert_marker(self, line): + if self._inline_insert_marker(line) is not None: + return True + return False + + def _inline_sanitize_line(self, line): + sanitized = line.expandtabs(self._context._template_tabsize).strip('\n') + return sanitized + + @staticmethod + def _inline_common_start(sa, sb): + """ returns the longest common substring from the beginning of sa and sb """ + def _iter(): + for a, b in zip(sa, sb): + if a == b: + yield a + else: + return + return ''.join(_iter()) + + @staticmethod + def _inline_common_prefix(lines): + if not lines: + return '' + while len(lines) > 1: + s1 = min(lines) + s2 = max(lines) + for i, c in enumerate(s1): + if c != s2[i]: + return s1[:i] + # We have a only prefix line (full s1) + # Remove all only prefix lines + # and try again if all lines start with space after the prefix + super_prefix_lines = [] + for line in lines: + if len(line) > len(s1): + if not line[len(s1)].isspace(): + return s1 + super_prefix_lines.append(line) + lines = super_prefix_lines + else: + return '' + + + ## + # @brief Re-indent a code block. + # + # Take a block of text as a string or list of lines. + # Remove any common prefix and whitespace indentation. + # Re-indent using new_indent + # + # @param lines + # @param common_prefix + # @param new_indent + # @return indented code block as a single string. + def _inline_reindent_code(self, lines, common_prefix = '', new_indent=''): + if not isinstance(lines, list): + lines = lines.splitlines() + prefix_len = len(common_prefix) + code = '' + for line in lines: + if len(line) <= prefix_len: + code += '\n' + else: + code += new_indent + line[prefix_len:] + '\n' + return code + + ## + # @brief Extract the executable script code from the snippet. + # + def _inline_snippet_code(self, fname, snippet, marker_lines): + # If the markers and lines all have the same prefix + # (end-of-line comment chars, for example), + # then remove it from all the lines. + lines = snippet.splitlines() + common_prefix = self._inline_common_prefix(marker_lines + lines) + if not common_prefix: + # there may be a prefix error + # just do some heuristics + if fname.endswith( + ('.h', '.hxx', '.c', '.cpp', '.cxx')): + # assume C/C++ comments + for line in (marker_lines + lines): + if line.strip().startswith('*'): + common_prefix = '*' + break + elif line.strip().startswith('//'): + common_prefix = '//' + break + if common_prefix: + # there should be a common prefix -> error + common_lines = list() # lines with correct prefix + template_eval_begin = self._context._eval_begin + for lineno, line in enumerate(lines): + if not line.strip().startswith(common_prefix): + print("Cogeno: Common prefix '{}' may miss in cogeno snippet '{}' ({}) {}.".format( + common_prefix, fname, lineno, template_eval_begin + lineno)) + line_start = lineno - 5 + if line_start < 0: + line_start = 0 + line_end = lineno + 5 + if line_end > len(lines): + line_end = len(lines) + for snippet_lineno in range(line_start, line_end): + template_lineno = template_eval_begin + snippet_lineno + print("#{} ({}): {}".format( + template_lineno, snippet_lineno, lines[snippet_lineno])) + else: + common_lines.append(line) + if len(common_lines) >= int(len(lines) / 2): + common_prefix = os.path.commonprefix(common_lines) + print("Cogeno: Assuming common prefix '{}' for cogeno snippet '{}' in '{}'.".format( + common_prefix, fname, self._context._template_file)) + else: + common_prefix = '' + code = self._inline_reindent_code(lines, common_prefix) + return code + + ## + # @brief evaluate inline template + # + def _inline_evaluate(self): + if not self._context.script_is_inline(): + # This should never happen + self.error("Unexpected context '{}' for inline evaluation." + .format(self._context.script_type()), + frame_index = -2, lineno = 0) + + self.log('s{}: process inline template {}' + .format(len(self.cogeno_module_states), + self._context._template_file)) + + state = 'text' + lines = self._context._template.splitlines() + snippet_context = None + marker_lines = None + for line_no, line in enumerate(lines): + if line_no < self._context._eval_begin: + # Someone exclude parts of the template + continue + if state == 'text': + if self._inline_is_start_marker(line): + self._context._eval_begin = line_no + 1 + self._context._eval_end = len(lines) + # prepare context for snippet processing + marker_lines = [self._inline_sanitize_line(line),] + if self._inline_start_marker(line) \ + == self._inline_python_start_marker: + script_type = 'python' + else: + script_type = 'jinja2' + if self._inline_is_end_marker(line): + # within the start marker line is also and end marker + self._context._eval_begin = line_no + self._context._eval_end = line_no + 1 + line = self._inline_sanitize_line(line) + # single line snippets contain the markers - remove them + start_marker = self._inline_start_marker(line) + end_marker = self._inline_end_marker(line) + begin = line.find(start_marker) + end = line.find(end_marker) + if begin > end: + self.error("Cogeno code markers inverted", + frame_index = -2) + else: + template = line[begin + len(start_marker): end].strip() + snippet_context._template += template + '\n' + if self._context._delete_code: + # cut out template code + line = line[:begin] + line[end + len(end_marker):] + self._context.outl(line) + state = 'snippet_insert' + else: + snippet_context = Context(self, \ + parent_context = self._context, + template_file = "{}#{}".format(self._context._template_file, line_no + 1), + template = '', + script_type = script_type, + template_source_type = 'snippet', + eval_begin = 0) + if not self._context._delete_code: + self._context.outl(line) + state = 'snippet_template' + elif self._inline_is_end_marker(line): + self._context._eval_end = line_no + 1 + self.error("Unexpected end marker in '{}'".format(line), + frame_index=-1, lineno=line_no) + elif self._inline_is_insert_marker(line): + self._context._eval_end = line_no + 1 + self.error("Unexpected insert marker in '{}'".format(line), + frame_index=-1, lineno=line_no) + else: + self._context.outl(line) + elif state == 'snippet_template': + if self._inline_is_start_marker(line): + self._context._eval_end = line_no + 1 + self.error("Unexpected start marker in '{}'".format(line), + frame_index=-1, lineno=line_no) + elif self._inline_is_end_marker(line): + self._context._eval_end = line_no + if not self._context._delete_code: + self._context.outl(line) + marker_lines.append(self._inline_sanitize_line(line)) + # We now have the template code. + # The following line maybe a trailing comment line + trailing_line = lines[line_no + 1] + common_start = self._inline_common_start(line, + trailing_line) + if (len(common_start) == 0) or common_start.isspace(): + state = 'snippet_insert' + elif '@code' in trailing_line: + state = 'snippet_insert' + elif len(common_start) == (len(trailing_line) - 1): + state = 'snippet_template_trailing' + elif len(common_start) == len(trailing_line): + state = 'snippet_template_trailing' + else: + state = 'snippet_insert' + elif self._inline_is_insert_marker(line): + self._context._eval_end = line_no + 1 + self.error("Unexpected insert marker in '{}'".format(line), + frame_index=-1, lineno=line_no) + else: + if not self._context._delete_code: + self._context.outl(line) + # expand tabs now as we may have to remove the common prefix + # which would change tab handling + line = self._inline_sanitize_line(line) + snippet_context._template += line + '\n' + elif state == 'snippet_template_trailing': + self._context.outl(line) + state = 'snippet_insert' + elif state == 'snippet_insert': + if self._inline_is_start_marker(line): + self._context._eval_end = line_no + 1 + self.error("Unexpected start marker in '{}'".format(line), + frame_index=-1, lineno=line_no) + elif self._inline_is_end_marker(line): + self._context._eval_end = line_no + 1 + self.error("Unexpected end marker in '{}'".format(line), + frame_index=-1, lineno=line_no) + elif self._inline_is_insert_marker(line): + state = 'text' + code = self._inline_snippet_code( \ + snippet_context._template_file, + snippet_context._template, + marker_lines) + if code and \ + not self._context._options.bNoGenerate and \ + self._context._globals['_generate_code']: + snippet_context._template = code + # let the devils work - evaluate context + # inserts the context output into the current context + self._evaluate_context(snippet_context) + # We need to make sure that the last line in the output + # ends with a newline, or it will be joined to the + # end-output line, ruining cogeno's idempotency. + if self._context._outstring \ + and self._context._outstring[-1] != '\n': + self._context.outl('') + if not self._context._delete_code: + self._context.outl(line) + self._context._eval_begin = line_no + 1 + self._context._eval_end = len(lines) + else: + # we ignore the code in between end marker and insert marker + pass + + if snippet_context is None: + # No snippet found - otherwise there would be a context + if self.options.bWarnEmpty: + self.warning("No inline code found in {}" + .format(self._context._template_file)) + elif state != 'text': + # The snippet was not fully evaluated + self.error("Missing '{}' or '{}' before end of template." + .format(self._inline_end_markers, self._inline_insert_markers), + frame_index = -2, + lineno = self._context._eval_begin) diff --git a/scripts/cogeno/cogeno/jinja2gen.py b/scripts/cogeno/cogeno/jinja2gen.py new file mode 100644 index 000000000000..5178bd2b83f5 --- /dev/null +++ b/scripts/cogeno/cogeno/jinja2gen.py @@ -0,0 +1,169 @@ +# +# Copyright (c) 2018 Nordic Semiconductor ASA +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import inspect +from pathlib import Path +from jinja2 import (Environment, ChoiceLoader, FileSystemLoader, BaseLoader, + TemplateSyntaxError, TemplateAssertionError) + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from error import Error + from context import Context +else: + # use current package visibility + from .error import Error + from .context import Context + +class Jinja2GenMixin(object): + __slots__ = [] + + class Jinja2SnippetLoader(BaseLoader): + ## + # Pool of known snippets + snippet_templates = dict() + + def __init__(self, template_name = None, template_code = None): + if not (template_name is None): + self.snippet_templates[template_name] = template_code + + def get_source(self, environment, template): + if not template in self.snippet_templates: + raise TemplateNotFound(template) + return self.snippet_templates[template], None, True + + ## + # @brief Render a Ninja2 template. + # + # @param template_spec + # @param data + def render(self, template_spec, data = None): + render_output = "" + env = self._jinja2_environment() + if data is None: + data = self._jinja2_data() + + # Save proxy connection to the generator cogeno module + cogeno_proxy_save = env.globals.get('cogeno', None) + + try: + # Provide our generator API to Jinja2 templates + env.globals.update(self._context.generator_globals()) + + # Assure there is a cogeno proxy object to this generator + # cogeno module available to the template + env.globals.update(cogeno = self.cogeno_module.__dict__) + + if (template_spec in self.Jinja2SnippetLoader.snippet_templates): + template_name = template_spec + template = env.get_template(template_spec) + elif Path(template_spec).is_file(): + template_name = template_spec + template = env.get_template(template_spec) + elif isinstance(template_spec, str): + template_name = 'string' + template = env.from_string(template_spec) + else: + raise self._get_error_exception("Unknown template spec {}".format(template_spec), + frame_index = -1, + snippet_lineno = self._snippet_lineno, + snippet_adjust = 0) + self.log('------- render start {}'.format(template_name)) + render_output = template.render(data=data) + self.log(gen) + self.log('------- render end {}'.format(template_name)) + + except (TemplateSyntaxError, TemplateAssertionError) as exc: + raise self._get_error_exception("{}".format(exc.message), + frame_index = -1, + snippet_lineno = exc.lineno, + snippet_adjust = 0) + finally: + # Restore proxy connection to generator + env.globals.update(cogen = cogeno_proxy_save) + + # We need to make sure that the last line in the output + # ends with a newline, or it will be joined to the + # end-output line, ruining cogen's idempotency. + if render_output and render_output[-1] != '\n': + render_output += '\n' + if not self._context.script_is_jinja2(): + # render was called from non jinja2 script context + # we have to fill _outstring + self._context.out(render_output) + # Jinja2 context requires to return the render result + # Do it always + return render_output + + def _jinja2_environment(self): + if self._context._jinja2_environment is None: + # Prepare Jinja2 execution environment + + # Jinja2 templates paths for Jinja2 file loader + templates = [] + for path in self.templates_paths(): + templates.append(str(path)) + + loader = ChoiceLoader([ + FileSystemLoader(templates), + self.Jinja2SnippetLoader()] + ) + env = Environment(loader=loader, + extensions=['jinja2.ext.do', + 'jinja2.ext.loopcontrols']) + self._context._jinja2_environment = env + + # Add path to the directory of the jinja2 template + path = Path(self._context._template_file) + if path.is_file(): + env.loader.loaders[0].searchpath.append(str(path.resolve().parent)) + + return self._context._jinja2_environment + + def _jinja2_data(self): + if not 'jinja2_data' in self._context._globals: + # Prepare Jinja2 data + data = {} + data['devicetree'] = self.edts()._edts + data['config'] = self.config_properties() + data['runtime'] = {} + data['runtime']['include_path'] = [] + for path in self.templates_paths(): + data['runtime']['include_path'].append(str(path)) + data['runtime']['input_file'] = self._context._template_file + data['runtime']['output_name'] = self._context._output_file + data['runtime']['log_file'] = self._context._log_file + data['runtime']['defines'] = self._context._options.defines + self._context._globals['jinja2_data'] = data + else: + data = self._context._globals['jinja2_data'] + return data + + ## + # @brief evaluate jinja2 template + # + # write evaluation result to self._outstring + # + def _jinja2_evaluate(self): + if not self._context.script_is_jinja2(): + # This should never happen + self.error("Unexpected context '{}' for inline evaluation." + .format(self._context.script_type()), + frame_index = -2, snippet_lineno = 0) + + self.log('s{}: process jinja2 template {}' + .format(len(self.cogeno_module_states), + self._context._template_file)) + + if not self._context.template_is_file(): + # Make the string/ snippet template known to the + # Snippet loader + self.Jinja2SnippetLoader.snippet_templates[self._context._template_file] = self._context._template + + render_output = self.render(self._context._template_file) + self.out(render_output) diff --git a/scripts/cogeno/cogeno/lock.py b/scripts/cogeno/cogeno/lock.py new file mode 100644 index 000000000000..8736a2683d81 --- /dev/null +++ b/scripts/cogeno/cogeno/lock.py @@ -0,0 +1,34 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from filelock import Timeout, FileLock +else: + # use current package visibility + from .filelock import Timeout, FileLock + +class LockMixin(object): + __slots__ = [] + + _lock = None + + ## + # @brief lock + # + # @return lock + # + def lock(self): + if self._lock is None: + self._lock = FileLock(self._context._lock_file) + return self._lock + + @property + def LockTimout(self): + return Timeout + diff --git a/scripts/cogeno/cogeno/log.py b/scripts/cogeno/cogeno/log.py new file mode 100644 index 000000000000..cc89a06d1d61 --- /dev/null +++ b/scripts/cogeno/cogeno/log.py @@ -0,0 +1,70 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +class LogMixin(object): + __slots__ = [] + + _log_file = None + + def log(self, message, message_type=None, end="\n", logonly=True): + if self._log_file is None: + if self._context._log_file is None: + # No file logging specified + pass + elif self._context._log_file == '-': + # log to stdout + pass + else: + log_file = Path(self._context._log_file) + try: + with self.lock().acquire(timeout = 10): + if not log_file.is_file(): + # log file will be created + # add preamble + preamble = "My preamble\n{}".format(message) + with log_file.open(mode = 'a', encoding = 'utf-8') as log_fd: + log_fd.write(preamble) + self._log_file = log_file + except self.LockTimeout: + # Something went really wrong - we did not get the lock + self.error( + "Log file '{}' no access." + .format(log_file), frame_index = 2) + except: + raise + if message_type is None: + message_type = '' + else: + message_type = message_type+': ' + if not self._log_file is None: + # Write message to log file + try: + with self.lock().acquire(timeout = 10): + with self._log_file.open(mode = 'a', encoding = 'utf-8') as log_fd: + for line in message.splitlines(): + log_fd.write("{}{}{}".format(message_type, line, end)) + log_fd.flush() + except Timeout: + # Something went really wrong - we did not get the lock + self.error( + "Log file '{}' no access." + .format(log_file), frame_index = 2) + except: + raise + if not logonly: + print(message_type+message, file=self._stderr, end=end) + + def msg(self, s): + self.log(s, message_type='message', logonly=False) + + def warning(self, s): + self.log(s, message_type='warning', logonly=False) + + def prout(self, s, end="\n"): + self.log(s, message_type=None, end=end, logonly=False) + + def prerr(self, s, end="\n"): + self.log(s, message_type='error', end=end, logonly=False) diff --git a/scripts/cogeno/cogeno/options.py b/scripts/cogeno/cogeno/options.py new file mode 100644 index 000000000000..7045446f2f25 --- /dev/null +++ b/scripts/cogeno/cogeno/options.py @@ -0,0 +1,260 @@ +# Copyright 2004-2016, Ned Batchelder. +# http://nedbatchelder.com/code/cog +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: MIT + +import os +import argparse +from pathlib import Path + +class Options(object): + + @staticmethod + def is_valid_directory(parser, arg): + try: + path = Path(arg).resolve() + except: + path = Path(arg) + if not path.is_dir(): + parser.error('The directory {} does not exist!'.format(path)) + else: + # File directory exists so return the directory + return str(path) + + @staticmethod + def is_valid_file(parser, arg): + try: + path = Path(arg).resolve() + except: + path = Path(arg) + if not path.is_file(): + parser.error('The file {} does not exist!'.format(path)) + else: + # File exists so return the file + return str(path) + + def __init__(self): + # Defaults for argument values. + self.args = [] + self.includePath = [] + self.defines = {} + self.bNoGenerate = False + self.sOutputName = None + self.bWarnEmpty = False + self.delete_code = False + self.bNewlines = False + self.sEncoding = "utf-8" + self.verbosity = 2 + + self.modules_paths = None + self.templates_paths = None + self.bindings_paths = None + self.input_file = None + self.output_file = None + self.log_file = None + self.lock_file = None + + self._parser = argparse.ArgumentParser( + description='Generate code with inlined Python code.') + self._parser.add_argument('-x', '--delete-code', + dest='delete_code', action='store_true', + help='Delete the generator code from the output file.') + self._parser.add_argument('-w', '--warn-empty', + dest='bWarnEmpty', action='store_true', + help='Warn if a file has no generator code in it.') + self._parser.add_argument('-n', '--encoding', nargs=1, + dest='sEncoding', action='store', metavar='ENCODING', + type=lambda x: self.is_valid_file(self._parser, x), + help='Use ENCODING when reading and writing files.') + self._parser.add_argument('-U', '--unix-newlines', + dest='bNewlines', action='store_true', + help='Write the output with Unix newlines (only LF line-endings).') + self._parser.add_argument('-D', '--define', nargs=1, metavar='DEFINE', + dest='defines', action='append', + help='Define a global string available to your generator code.') + self._parser.add_argument('-m', '--modules', nargs='+', metavar='DIR', + dest='modules_paths', action='append', + help='Use modules from modules DIR. We allow multiple') + self._parser.add_argument('-t', '--templates', nargs='+', metavar='DIR', + dest='templates_paths', action='append', + help='Use templates from templates DIR. We allow multiple') + self._parser.add_argument('-c', '--config', nargs=1, metavar='FILE', + dest='config_file', action='store', + help='Use configuration variables from configuration FILE.') + self._parser.add_argument('-a', '--cmakecache', nargs=1, metavar='FILE', + dest='cmakecache_file', action='store', + type=lambda x: self.is_valid_file(self._parser, x), + help='Use CMake variables from CMake cache FILE.') + self._parser.add_argument('-d', '--dts', nargs=1, metavar='FILE', + dest='dts_file', action='store', + type=lambda x: self.is_valid_file(self._parser, x), + help='Load the device tree specification from FILE.') + self._parser.add_argument('-b', '--bindings', nargs='+', metavar='DIR', + dest='bindings_paths', action='store', + type=lambda x: self.is_valid_directory(self._parser, x), + help='Use bindings from bindings DIR for device tree extraction.' + + ' We allow multiple') + self._parser.add_argument('-e', '--edts', nargs=1, metavar='FILE', + dest='edts_file', action='store', + help='Write or read EDTS database to/ from FILE.') + self._parser.add_argument('-i', '--input', nargs=1, metavar='FILE', + dest='input_file', action='store', + type=lambda x: self.is_valid_file(self._parser, x), + help='Get the input from FILE.') + self._parser.add_argument('-o', '--output', nargs=1, metavar='FILE', + dest='sOutputName', action='store', + help='Write the output to FILE.') + self._parser.add_argument('-l', '--log', nargs=1, metavar='FILE', + dest='log_file', action='store', + help='Log to FILE.') + self._parser.add_argument('-k', '--lock', nargs=1, metavar='FILE', + dest='lock_file', action='store', + help='Use lock FILE for concurrent runs of cogeno.') + + def __str__(self): + sb = [] + for key in self.__dict__: + sb.append("{key}='{value}'".format(key=key, value=self.__dict__[key])) + return ', '.join(sb) + + def __repr__(self): + return self.__str__() + + + def __eq__(self, other): + """ Comparison operator for tests to use. + """ + return self.__dict__ == other.__dict__ + + def clone(self): + """ Make a clone of these options, for further refinement. + """ + return copy.deepcopy(self) + + def parse_args(self, argv): + args = self._parser.parse_args(argv) + # set options + self.delete_code = args.delete_code + self.bWarnEmpty = args.bWarnEmpty + self.bNewlines = args.bNewlines + # --modules + self.modules_paths = [] + if args.modules_paths is not None: + paths = args.modules_paths + if not isinstance(paths, list): + paths = [paths] + if isinstance(paths[0], list): + paths = [item for sublist in paths for item in sublist] + for path in paths: + try: + path = Path(path).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + path = Path(path) + if path.is_dir(): + self.modules_paths.append(path) + elif path.is_file(): + self.modules_paths.append(path.parent) + else: + print("options.py: Unknown modules path", path, "- ignored") + # --templates + self.templates_paths = [] + if args.templates_paths is not None: + paths = args.templates_paths + if not isinstance(paths, list): + paths = [paths] + if isinstance(paths[0], list): + paths = [item for sublist in paths for item in sublist] + for path in paths: + try: + path = Path(path).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + path = Path(path) + if path.is_dir(): + self.templates_paths.append(path) + elif path.is_file(): + self.templates_paths.append(path.parent) + else: + print("options.py: Unknown templates path", path, "- ignored") + # --config + if args.config_file is None: + self.config_file = None + else: + self.config_file = args.config_file[0] + # --cmakecache + if args.cmakecache_file is None: + self.cmakecache_file = None + else: + self.cmakecache_file = args.config_file[0] + # --dts + if args.dts_file is None: + self.dts_file = None + else: + self.dts_file = args.dts_file[0] + # --bindings + self.bindings_paths = [] + if args.bindings_paths is not None: + paths = args.bindings_paths + if not isinstance(paths, list): + paths = [paths] + if isinstance(paths[0], list): + paths = [item for sublist in paths for item in sublist] + for path in paths: + try: + path = Path(path).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + path = Path(path) + if path.is_dir(): + self.bindings_paths.append(path) + elif path.is_file(): + self.bindings_paths.append(path.parent) + else: + print("options.py: Unknown bindings path", path, "- ignored") + # --edts + if args.edts_file is None: + self.edts_file = None + else: + self.edts_file = args.edts_file[0] + # --input + if args.input_file is None: + self.input_file = None + else: + self.input_file = args.input_file[0] + # --output + if args.sOutputName is None: + self.sOutputName = None + else: + self.sOutputName = args.sOutputName[0] + # --log_file + if args.log_file is None: + self.log_file = None + else: + self.log_file = args.log_file[0] + # --lock_file + if args.lock_file is None: + self.lock_file = None + else: + self.lock_file = args.lock_file[0] + # --defines + self.defines = {} + if args.defines is not None: + for define in args.defines: + d = define[0].split('=') + if len(d) > 1: + value = d[1] + else: + value = None + self.defines[d[0]] = value + + +class OptionsMixin(object): + __slots__ = [] + + def option(self, option_name): + return getattr(self._context._options, option_name) diff --git a/scripts/cogeno/cogeno/output.py b/scripts/cogeno/cogeno/output.py new file mode 100644 index 000000000000..b7c4c52d12d3 --- /dev/null +++ b/scripts/cogeno/cogeno/output.py @@ -0,0 +1,34 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + + +class OutputMixin(object): + __slots__ = [] + + def _out(self, output='', dedent=False, trimblanklines=False): + if trimblanklines and ('\n' in output): + lines = output.split('\n') + if lines[0].strip() == '': + del lines[0] + if lines and lines[-1].strip() == '': + del lines[-1] + output = '\n'.join(lines)+'\n' + if dedent: + output = reindentBlock(output) + + if self._context.script_is_python(): + self._context._outstring += output + return output + + def msg(self, s): + return self.prout("Message: "+s) + + def out(self, sOut='', dedent=False, trimblanklines=False): + return self._out(sOut, dedent, trimblanklines) + + def outl(self, sOut='', **kw): + """ The cog.outl function. + """ + self._out(sOut, **kw) + self._out('\n') diff --git a/scripts/cogeno/cogeno/paths.py b/scripts/cogeno/cogeno/paths.py new file mode 100644 index 000000000000..b179e83b8bfa --- /dev/null +++ b/scripts/cogeno/cogeno/paths.py @@ -0,0 +1,81 @@ +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import sys +from pathlib import Path + +class PathsMixin(object): + __slots__ = [] + + _modules_paths = None + _templates_paths = None + + @staticmethod + def cogeno_path(): + return Path(__file__).resolve().parent + + @staticmethod + def find_file_path(file_name, paths): + # Assure we have a string here + file_name = str(file_name) + try: + file_path = Path(file_name).resolve() + except FileNotFoundError: + # Python 3.4/3.5 will throw this exception + # Python >= 3.6 will not throw this exception + file_path = Path(file_name) + if file_path.is_file(): + return file_path + + # don't resolve upfront + file_dir_parts = list(Path(file_name).parent.parts) + for path in paths: + file_path = None + if len(file_dir_parts) > 0: + path_parts = list(path.parts) + common_start_count = path_parts.count(file_dir_parts[0]) + common_start_index = 0 + while common_start_count > 0: + common_start_count -= 1 + common_start_index = path_parts.index(file_dir_parts[0], common_start_index) + common_length = len(path_parts) - common_start_index + if common_length > len(file_dir_parts): + # does not fit + continue + if path_parts[common_start_index:] == file_dir_parts[0:common_length]: + # We have a common part + file_path = path.parents[common_length - 1].joinpath(file_name) + if file_path.is_file(): + # we got it + return file_path + else: + # may be there is another combination + file_path = None + if file_path is None: + # Just append file path to path + file_path = path.joinpath(file_name) + if file_path.is_file(): + return file_path + return None + + def modules_paths_append(self, path): + self._context._modules_paths.append(Path(path)) + self.cogeno_module.path.extend(str(path)) + sys.path.extend(str(path)) + + def modules_paths(self): + return self._context._modules_paths + + def templates_paths_append(self, path): + self._context._templates_paths.append(Path(path)) + + def templates_paths(self): + return self._context._templates_paths + + def bindings_paths(self): + return self._context._options.bindings_paths + + + + diff --git a/scripts/cogeno/cogeno/pygen.py b/scripts/cogeno/cogeno/pygen.py new file mode 100644 index 000000000000..96e279f6188c --- /dev/null +++ b/scripts/cogeno/cogeno/pygen.py @@ -0,0 +1,81 @@ +# Copyright (c) 2018 Nordic Semiconductor ASA +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +import sys +from traceback import TracebackException + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from error import Error + from context import Context +else: + # use current package visibility + from .error import Error + from .context import Context + +class PyGenMixin(object): + __slots__ = [] + + ## + # @brief evaluate + # + def _python_evaluate(self): + if not self._context.script_is_python(): + # This should never happen + self.error("Unexpected context '{}' for python evaluation." + .format(self._context.script_type()), + frame_index = -2, snippet_lineno = 0) + + self.log('s{}: process python template {}' + .format(len(self.cogeno_module_states), + self._context._template_file)) + + # we add an extra line 'import cogeno' + # account for that + self._context._eval_adjust = -1 + + # In Python 2.2, the last line has to end in a newline. + eval_code = "import cogeno\n" + self._context._template + "\n" + + try: + code = compile(eval_code, self._context._template_file, 'exec') + except: + exc_type, exc_value, exc_tb = sys.exc_info() + exc_traceback = TracebackException(exc_type, exc_value, exc_tb) + self.error( + "compile exception '{}' within template {}".format( + exc_value, self._context._template_file), + frame_index = -2, + lineno = exc_traceback.lineno) + + # Make sure the "cogeno" module has our state. + self._set_cogeno_module_state() + + self._context._outstring = '' + try: + eval(code, self._context.generation_globals()) + except: + exc_type, exc_value, exc_tb = sys.exc_info() + if exc_type is Error: + # Exception raise by cogeno means + raise + # Not raised by cogeno means - add some info + print("Cogeno: eval exception within cogeno snippet '{}' in '{}'.".format( + self._context._template_file, self._context.parent()._template_file)) + print(self._list_snippet()) + raise + finally: + self._restore_cogeno_module_state() + + # We need to make sure that the last line in the output + # ends with a newline, or it will be joined to the + # end-output line, ruining cogen's idempotency. + if self._context._outstring and self._context._outstring[-1] != '\n': + self._context._outstring += '\n' + + # end of evaluation - no extra offset anymore + self._eval_adjust = 0 diff --git a/scripts/cogeno/cogeno/redirectable.py b/scripts/cogeno/cogeno/redirectable.py new file mode 100644 index 000000000000..641e9b8adb5f --- /dev/null +++ b/scripts/cogeno/cogeno/redirectable.py @@ -0,0 +1,31 @@ +# Copyright 2004-2016, Ned Batchelder. +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: MIT + +import sys + +class RedirectableMixin(object): + __slots__ = [] + + def set_standard_streams(self, stdout=None, stderr=None): + """ Assign new files for standard out and/or standard error. + """ + if stdout: + self._stdout = stdout + if stderr: + self._stderr = stderr + + def prout(self, s, end="\n"): + print(s, file=self._stdout, end=end) + + def prerr(self, s, end="\n"): + print(s, file=self._stderr, end=end) + + +class Redirectable(RedirectableMixin): + """ An object with its own stdout and stderr files. + """ + def __init__(self): + self._stdout = sys.stdout + self._stderr = sys.stderr diff --git a/scripts/cogeno/cogeno/whiteutils.py b/scripts/cogeno/cogeno/whiteutils.py new file mode 100644 index 000000000000..2a128e4ab88f --- /dev/null +++ b/scripts/cogeno/cogeno/whiteutils.py @@ -0,0 +1,72 @@ +# Copyright 2004-2016, Ned Batchelder. +# http://nedbatchelder.com/code/cog +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: MIT + +import re + +def b(s): + return s.encode("latin-1") + +def whitePrefix(strings): + """ Determine the whitespace prefix common to all non-blank lines + in the argument list. + """ + # Remove all blank lines from the list + strings = [s for s in strings if s.strip() != ''] + + if not strings: return '' + + # Find initial whitespace chunk in the first line. + # This is the best prefix we can hope for. + pat = r'\s*' + if isinstance(strings[0], (bytes, )): + pat = pat.encode("utf8") + prefix = re.match(pat, strings[0]).group(0) + + # Loop over the other strings, keeping only as much of + # the prefix as matches each string. + for s in strings: + for i in range(len(prefix)): + if prefix[i] != s[i]: + prefix = prefix[:i] + break + return prefix + +def reindentBlock(lines, newIndent=''): + """ Take a block of text as a string or list of lines. + Remove any common whitespace indentation. + Re-indent using newIndent, and return it as a single string. + """ + sep, nothing = '\n', '' + if isinstance(lines, (bytes, )): + sep, nothing = b('\n'), b('') + if isinstance(lines, (str, bytes)): + lines = lines.split(sep) + oldIndent = whitePrefix(lines) + outLines = [] + for l in lines: + if oldIndent: + l = l.replace(oldIndent, nothing, 1) + if l and newIndent: + l = newIndent + l + outLines.append(l) + return sep.join(outLines) + +def commonPrefix(strings): + """ Find the longest string that is a prefix of all the strings. + """ + if not strings: + return '' + prefix = strings[0] + for s in strings: + if len(s) < len(prefix): + prefix = prefix[:len(s)] + if not prefix: + return '' + for i in range(len(prefix)): + if prefix[i] != s[i]: + prefix = prefix[:i] + break + return prefix From bd05cb5866014bcc911fdc95db49ea37cc4b0536 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 19 Oct 2018 18:51:16 +0200 Subject: [PATCH 02/13] scripts: cogeno: edts: add extended DTS database module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Extended Device Tree Specification database collates device tree (dts) information with information taken from the device tree bindings. The EDTS database may be loaded from a json file, stored to a json file or extracted from the DTS files and the bindings yaml files. The database is integrated into cogeno as a module. The commit integrates database development done in #9876 which was based on #6762. Major differences/ improvements to #9876 are: - the database now has an own extraction function that can be used instead of e.g. extract_dts_includes. The extraction function follows the design of the extract_dts_includes script and the additions that were done in #9876. It is restructured and several globals are now classes and objects. All functionality of extract_dts_includes related to the generation of defines is not part of the database extract function. It's sole purpose is to fill the database directly from the compiled DTS file. - the database got itÅ› own directory 'edtsdb' to structure all files related to the database. - The EDTSDevice class from #9876 was enhanced to allow devices to access the database they are taken from. Mayor compatibility issues to #9876. - The consumer, provider API and the internal structure of the database is copied from #9876. - API should be fully compatible. - Extraction of children was replaced as it broke the concept of the devices struct as a list of devices. The functions device.get_children() and device.get_parent() may be used to acess the parent<->child relation. - The EDTSDevice class is copied from #9876. - The device API should be compatible except for - the constructor which now needs the EDTS database and - the unique id feature. To ge an unique id the device.get_name() function can be used instead. Signed off from #9876 added to attribute for the changes done there and copied. Signed-off-by: Erwan Gouriou Signed-off-by: Kumar Gala Signed-off-by: Bobby Noelte --- scripts/cogeno/cogeno/modules/__init__.py | 7 + scripts/cogeno/cogeno/modules/edtsdatabase.py | 106 ++++++ .../cogeno/cogeno/modules/edtsdb/__init__.py | 7 + .../cogeno/cogeno/modules/edtsdb/binder.py | 217 ++++++++++++ .../cogeno/cogeno/modules/edtsdb/consumer.py | 102 ++++++ .../cogeno/cogeno/modules/edtsdb/database.py | 63 ++++ .../cogeno/cogeno/modules/edtsdb/device.py | 198 +++++++++++ .../modules/edtsdb/devicetree/aliases.py | 24 ++ .../modules/edtsdb/devicetree/chosen.py | 19 + .../modules/edtsdb/devicetree/compatibles.py | 32 ++ .../modules/edtsdb/devicetree/parser.py | 260 ++++++++++++++ .../modules/edtsdb/devicetree/phandles.py | 32 ++ .../modules/edtsdb/devicetree/reduced.py | 30 ++ scripts/cogeno/cogeno/modules/edtsdb/dts.py | 188 ++++++++++ .../cogeno/cogeno/modules/edtsdb/extractor.py | 139 ++++++++ .../modules/edtsdb/extractors/clocks.py | 330 ++++++++++++++++++ .../modules/edtsdb/extractors/compatible.py | 40 +++ .../modules/edtsdb/extractors/controller.py | 32 ++ .../modules/edtsdb/extractors/default.py | 31 ++ .../modules/edtsdb/extractors/directive.py | 109 ++++++ .../cogeno/modules/edtsdb/extractors/dma.py | 158 +++++++++ .../modules/edtsdb/extractors/extractors.py | 78 +++++ .../cogeno/modules/edtsdb/extractors/flash.py | 105 ++++++ .../cogeno/modules/edtsdb/extractors/gpio.py | 172 +++++++++ .../modules/edtsdb/extractors/heuristics.py | 123 +++++++ .../modules/edtsdb/extractors/interrupts.py | 88 +++++ .../modules/edtsdb/extractors/pinctrl.py | 131 +++++++ .../cogeno/modules/edtsdb/extractors/pwm.py | 145 ++++++++ .../cogeno/modules/edtsdb/extractors/reg.py | 74 ++++ .../cogeno/modules/edtsdb/headermaker.py | 48 +++ .../cogeno/cogeno/modules/edtsdb/provider.py | 159 +++++++++ 31 files changed, 3247 insertions(+) create mode 100644 scripts/cogeno/cogeno/modules/__init__.py create mode 100755 scripts/cogeno/cogeno/modules/edtsdatabase.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/__init__.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/binder.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/consumer.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/database.py create mode 100755 scripts/cogeno/cogeno/modules/edtsdb/device.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/devicetree/aliases.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/devicetree/chosen.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/devicetree/compatibles.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/devicetree/parser.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/devicetree/phandles.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/devicetree/reduced.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/dts.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractor.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/clocks.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/compatible.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/controller.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/default.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/directive.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/dma.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/extractors.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/flash.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/gpio.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/heuristics.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/interrupts.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/pinctrl.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/pwm.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/extractors/reg.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/headermaker.py create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/provider.py diff --git a/scripts/cogeno/cogeno/modules/__init__.py b/scripts/cogeno/cogeno/modules/__init__.py new file mode 100644 index 000000000000..55a39fab19ef --- /dev/null +++ b/scripts/cogeno/cogeno/modules/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) 2017 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Empty to allow all modules to be imported diff --git a/scripts/cogeno/cogeno/modules/edtsdatabase.py b/scripts/cogeno/cogeno/modules/edtsdatabase.py new file mode 100755 index 000000000000..31705809845b --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdatabase.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018 Bobby Noelte +# Copyright (c) 2018 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +import sys +import argparse +from pathlib import Path +from pprint import pprint + +## +# Make relative import work also with __main__ +if __package__ is None or __package__ == '': + # use current directory visibility + from edtsdb.database import EDTSDb + from edtsdb.device import EDTSDevice +else: + # use current package visibility + from .edtsdb.database import EDTSDb + from .edtsdb.device import EDTSDevice + + +## +# @brief Extended DTS database +# +class EDTSDatabase(EDTSDb): + + @staticmethod + def is_valid_directory(parser, arg): + try: + path = Path(arg).resolve() + except: + path = Path(arg) + if not path.is_dir(): + parser.error('The directory {} does not exist!'.format(path)) + else: + # File directory exists so return the directory + return str(path) + + @staticmethod + def is_valid_file(parser, arg): + try: + path = Path(arg).resolve() + except: + path = Path(arg) + if not path.is_file(): + parser.error('The file {} does not exist!'.format(path)) + else: + # File exists so return the file + return str(path) + + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + + def callable_main(self, args): + self._parser = argparse.ArgumentParser( + description='Extended Device Tree Specification Database.') + self._parser.add_argument('-l', '--load', nargs=1, metavar='FILE', + dest='load_file', action='store', + type=lambda x: EDTSDatabase.is_valid_file(self._parser, x), + help='Load the input from FILE.') + self._parser.add_argument('-s', '--save', nargs=1, metavar='FILE', + dest='save_file', action='store', + type=lambda x: EDTSDatabase.is_valid_file(self._parser, x), + help='Save the database to Json FILE.') + self._parser.add_argument('-i', '--export-header', nargs=1, metavar='FILE', + dest='export_header', action='store', + type=lambda x: EDTSDatabase.is_valid_file(self._parser, x), + help='Export the database to header FILE.') + self._parser.add_argument('-e', '--extract', nargs=1, metavar='FILE', + dest='extract_file', action='store', + type=lambda x: EDTSDatabase.is_valid_file(self._parser, x), + help='Extract the database from dts FILE.') + self._parser.add_argument('-b', '--bindings', nargs='+', metavar='DIR', + dest='bindings_dirs', action='store', + type=lambda x: EDTSDatabase.is_valid_directory(self._parser, x), + help='Use bindings from bindings DIR for extraction.' + + ' We allow multiple') + self._parser.add_argument('-p', '--print', + dest='print_it', action='store_true', + help='Print EDTS database content.') + + args = self._parser.parse_args(args) + + if args.load_file is not None: + self.load(args.load_file[0]) + if args.extract_file is not None: + self.extract(args.extract_file[0], args.bindings_dirs) + if args.save_file is not None: + self.save(args.save_file[0]) + if args.export_header is not None: + self.export_header(args.export_header[0]) + if args.print_it: + pprint(self._edts) + + return 0 + +def main(): + EDTSDatabase().callable_main(sys.argv[1:]) + +if __name__ == '__main__': + main() + diff --git a/scripts/cogeno/cogeno/modules/edtsdb/__init__.py b/scripts/cogeno/cogeno/modules/edtsdb/__init__.py new file mode 100644 index 000000000000..55a39fab19ef --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) 2017 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Empty to allow all modules to be imported diff --git a/scripts/cogeno/cogeno/modules/edtsdb/binder.py b/scripts/cogeno/cogeno/modules/edtsdb/binder.py new file mode 100644 index 000000000000..163e5155e671 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/binder.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017, Linaro Limited +# Copyright (c) 2018, Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +import os, fnmatch +import re +import yaml +import collections + +from pathlib import Path + +class Binder(yaml.Loader): + + ## + # List of all yaml files available for yaml loaders + # of this class. Must be preset before the first + # load operation. + _files = [] + + ## + # Files that are already included. + # Must be reset on the load of every new binding + _included = [] + + @staticmethod + def dict_merge(dct, merge_dct): + # from https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 + + """ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of + updating only top-level keys, dict_merge recurses down into dicts nested + to an arbitrary depth, updating keys. The ``merge_dct`` is merged into + ``dct``. + :param dct: dict onto which the merge is executed + :param merge_dct: dct merged into dct + :return: None + """ + for k, v in merge_dct.items(): + if (k in dct and isinstance(dct[k], dict) + and isinstance(merge_dct[k], collections.Mapping)): + Binder.dict_merge(dct[k], merge_dct[k]) + else: + if k in dct and dct[k] != merge_dct[k]: + print("binder.py: Merge of '{}': '{}' overwrites '{}'.".format( + k, merge_dct[k], dct[k])) + dct[k] = merge_dct[k] + + @classmethod + def _traverse_inherited(cls, node): + """ Recursive overload procedure inside ``node`` + ``inherits`` section is searched for and used as node base when found. + Base values are then overloaded by node values + and some consistency checks are done. + :param node: + :return: node + """ + + # do some consistency checks. Especially id is needed for further + # processing. title must be first to check. + if 'title' not in node: + # If 'title' is missing, make fault finding more easy. + # Give a hint what node we are looking at. + print("binder.py: node without 'title' -", node) + for prop in ('title', 'version', 'description'): + if prop not in node: + node[prop] = "".format(prop) + print("binder.py: WARNING:", + "'{}' property missing in".format(prop), + "'{}' binding. Using '{}'.".format(node['title'], + node[prop])) + + # warn if we have an 'id' field + if 'id' in node: + print("binder.py: WARNING: id field set", + "in '{}', should be removed.".format(node['title'])) + + if 'inherits' in node: + if isinstance(node['inherits'], list): + inherits_list = node['inherits'] + else: + inherits_list = [node['inherits'],] + node.pop('inherits') + for inherits in inherits_list: + if 'inherits' in inherits: + inherits = cls._traverse_inherited(inherits) + if 'type' in inherits: + if 'type' not in node: + node['type'] = [] + if not isinstance(node['type'], list): + node['type'] = [node['type'],] + if isinstance(inherits['type'], list): + node['type'].extend(inherits['type']) + else: + node['type'].append(inherits['type']) + + # type, title, description, version of inherited node + # are overwritten by intention. Remove to prevent dct_merge to + # complain about duplicates. + inherits.pop('type', None) + inherits.pop('title', None) + inherits.pop('version', None) + inherits.pop('description', None) + cls.dict_merge(inherits, node) + node = inherits + return node + + @classmethod + def _collapse_inherited(cls, bindings_list): + collapsed = dict(bindings_list) + + for k, v in collapsed.items(): + v = cls._traverse_inherited(v) + collapsed[k]=v + + return collapsed + + ## + # @brief Get bindings for given compatibles. + # + # @param compatibles + # @param bindings_paths directories to search for binding files + # @return dictionary of bindings found + @classmethod + def bindings(cls, compatibles, bindings_paths): + # find unique set of compatibles across all active nodes + s = set() + for k, v in compatibles.items(): + if isinstance(v, list): + for item in v: + s.add(item) + else: + s.add(v) + + # scan YAML files and find the ones we are interested in + # We add our own bindings directory last (lowest priority) + # We only allow one binding file with the same name + bindings_paths.append(Path(Path(__file__).resolve().parent, + 'bindings')) + cls._files = [] + binding_files = [] + for path in bindings_paths: + for root, dirnames, filenames in os.walk(str(path)): + for filename in fnmatch.filter(filenames, '*.yaml'): + if not filename in binding_files: + binding_files.append(filename) + cls._files.append(os.path.join(root, filename)) + + bindings_list = {} + file_load_list = set() + for file in cls._files: + for line in open(file, 'r', encoding='utf-8'): + if re.search('^\s+constraint:*', line): + c = line.split(':')[1].strip() + c = c.strip('"') + if c in s: + if file not in file_load_list: + file_load_list.add(file) + with open(file, 'r', encoding='utf-8') as yf: + cls._included = [] + bindings_list[c] = yaml.load(yf, cls) + + # collapse the bindings inherited information before return + return cls._collapse_inherited(bindings_list) + + def __init__(self, stream): + filepath = os.path.realpath(stream.name) + if filepath in self._included: + print("Error:: circular inclusion for file name '{}'". + format(stream.name)) + raise yaml.constructor.ConstructorError + self._included.append(filepath) + super(Binder, self).__init__(stream) + Binder.add_constructor('!include', Binder._include) + Binder.add_constructor('!import', Binder._include) + + def _include(self, node): + if isinstance(node, yaml.ScalarNode): + return self._extract_file(self.construct_scalar(node)) + + elif isinstance(node, yaml.SequenceNode): + result = [] + for filename in self.construct_sequence(node): + result.append(self._extract_file(filename)) + return result + + elif isinstance(node, yaml.MappingNode): + result = {} + for k, v in self.construct_mapping(node).iteritems(): + result[k] = self._extract_file(v) + return result + + else: + print("Error: unrecognised node type in !include statement") + raise yaml.constructor.ConstructorError + + def _extract_file(self, filename): + filepaths = [filepath for filepath in self._files if filepath.endswith(filename)] + if len(filepaths) == 0: + print("Error: unknown file name '{}' in !include statement". + format(filename)) + raise yaml.constructor.ConstructorError + elif len(filepaths) > 1: + # multiple candidates for filename + files = [] + for filepath in filepaths: + if os.path.basename(filename) == os.path.basename(filepath): + files.append(filepath) + if len(files) > 1: + print("Error: multiple candidates for file name '{}' in !include statement". + format(filename), filepaths) + raise yaml.constructor.ConstructorError + filepaths = files + with open(filepaths[0], 'r', encoding='utf-8') as f: + return yaml.load(f, Binder) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/consumer.py b/scripts/cogeno/cogeno/modules/edtsdb/consumer.py new file mode 100644 index 000000000000..59941781e9d8 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/consumer.py @@ -0,0 +1,102 @@ +# +# Copyright (c) 2018 Bobby Noelte +# Copyright (c) 2018 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +import json +from pathlib import Path + +from .device import EDTSDevice + +## +# @brief ETDS Database consumer +# +# Methods for ETDS database usage. +# +class EDTSConsumerMixin(object): + __slots__ = [] + + ## + # @brief Get compatibles + # + # @param None + # @return edts 'compatibles' dict + def get_compatibles(self): + return self._edts['compatibles'] + + ## + # @brief Get aliases + # + # @param None + # @return edts 'aliases' dict + def get_aliases(self): + return self._edts['aliases'] + + ## + # @brief Get chosen + # + # @param None + # @return edts 'chosen' dict + def get_chosen(self): + return self._edts['chosen'] + + ## + # @brief Get device types + # + # @param None + # @return edts device types dict + def get_device_types(self): + return self._edts['device-types'] + + ## + # @brief Get controllers + # + # @param None + # @return compatible generic device type + def get_controllers(self): + return self._edts['controllers'] + + ## + # @brief Get device ids of all activated compatible devices. + # + # @param compatibles compatible(s) + # @return list of device ids of activated devices that are compatible + def get_device_ids_by_compatible(self, compatibles): + device_ids = dict() + if not isinstance(compatibles, list): + compatibles = [compatibles, ] + for compatible in compatibles: + for device_id in self._edts['compatibles'].get(compatible, []): + device_ids[device_id] = 1 + return list(device_ids.keys()) + + ## + # @brief Get device id of activated device with given label. + # + # @return device id + def get_device_id_by_label(self, label): + for device_id, device in self._edts['devices'].items(): + if label == device.get('label', None): + return device_id + print("consumer.py: Device with label", + "'{}' not available in EDTS".format(label)) + return None + + ## + # @brief Get the device dict matching a device_id. + # + # @param device_id + # @return (dict)device + def get_device_by_device_id(self, device_id): + try: + return EDTSDevice(self, device_id) + except: + print("consumer.py: Device with device id", + "'{}' not available in EDTS".format(device_id)) + return None + + def load(self, file_path): + with Path(file_path).open(mode = "r", encoding="utf-8") as load_file: + self._edts = json.load(load_file) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/database.py b/scripts/cogeno/cogeno/modules/edtsdb/database.py new file mode 100644 index 000000000000..247fcab970e9 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/database.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2018 Linaro Limited +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from pathlib import Path +from collections.abc import Mapping + +from .consumer import EDTSConsumerMixin +from .provider import EDTSProviderMixin +from .extractor import EDTSExtractorMixin +from .headermaker import EDTSHeaderMakerMixin + +## +# @brief Extended DTS database +# +# Database schema: +# +# _edts dict( +# 'aliases': dict(alias) : sorted list(device-id)), +# 'chosen': dict(chosen), +# 'devices': dict(device-id : device-struct), +# 'compatibles': dict(compatible : sorted list(device-id)), +# 'device-types': dict(device-type : sorted list(compatible)), +# ... +# ) +# +# device-struct dict( +# 'device-id' : device-id, +# 'compatible' : list(compatible) or compatible, +# 'label' : label, +# property-name : property-value ... +# ) +# +# Database types: +# +# device-id: opaque id for a device (do not use for other purposes), +# compatible: any of ['st,stm32-spi-fifo', ...] - 'compatibe' from .yaml +# label: any of ['UART_0', 'SPI_11', ...] - label directive from DTS +# +class EDTSDb(EDTSConsumerMixin, EDTSProviderMixin, EDTSExtractorMixin, Mapping): + + def __init__(self, *args, **kw): + self._edts = dict(*args, **kw) + # setup basic database schema + for edts_key in ('devices', 'compatibles', 'aliases', 'chosen', + 'device-types', 'controllers'): + if not edts_key in self._edts: + self._edts[edts_key] = dict() + # Pool of extractors used by extract() + self._extractors = None + + def __getitem__(self, key): + return self._edts[key] + + def __iter__(self): + return iter(self._edts) + + def __len__(self): + return len(self._edts) + diff --git a/scripts/cogeno/cogeno/modules/edtsdb/device.py b/scripts/cogeno/cogeno/modules/edtsdb/device.py new file mode 100755 index 000000000000..968eef77a7ee --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/device.py @@ -0,0 +1,198 @@ +# +# Copyright (c) 2018 Linaro Limited +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +class EDTSDevice(object): + + ## + # @brief Init Device + # + # @param edts EDTS database + # @param device id Device ID within EDTS database + def __init__(self, edts, device_id): + if not (device_id in edts['devices']): + raise Exception("Device with device id '{}' not available in EDTS" + .format(device_id)) + self._device_id = device_id + self._edts = edts + + def get_device_id(self): + return self._device_id + + ## + # @brief Get devices that are children of this device. + # + # Returns a list of devices that are children of this device. + # + # @return List of devices, maybe empty. + def get_children(self): + child_devices = self.get_property('child-devices', None) + if child_devices is None: + return [] + children = [] + for i in child_devices: + children.append(EDTSDevice(self._edts, child_devices[i])) + return children + + ## + # @brief Get parent device of this device. + # + # @return Parent device, maybe None. + def get_parent(self): + parent_device = self.get_property('parent-device', None) + if parent_device is None: + return None + return EDTSDevice(self._edts, parent_device) + + ## + # @brief Get device name + # + # Returns a unique name for the device. The name is sanitized to be used + # in C code and allows easy identification of the device. + # + # Device name is generated from + # - device compatible + # - bus master address if the device is connected to a bus master + # - device address + # - parent device address if the device does not have a device address + # - label if the parent also does not have a device address or there is + # no parent + # + # @return device name + def get_name(self): + device_name = self.get_property('compatible/0', None) + if device_name is None: + raise Exception("No compatible property for device id '{}'." + .format(device_id)) + + bus_master_device_id = self.get_property('bus/master', None) + if bus_master_device_id is not None: + reg = self._edts.get_device_property(bus_master_device_id, 'reg') + try: + # use always the first key to get first address inserted into dict + # because reg_index may be number or name + # reg//address/ : address + for reg_index in reg: + for address_index in reg[reg_index]['address']: + bus = reg[reg_index]['address'][address_index] + device_name += '_' + hex(bus)[2:].zfill(8) + break + break + except: + # this device is missing the register directive + raise Exception("No bus master register address property for device id '{}'." + .format(bus_master_device_id)) + + reg = self.get_property('reg', None) + if reg is None: + # no reg property - take the reg property of the parent device + parent_device_id = self.get_property('parent-device', None) + if parent_device_id: + parent_device = self._edts.get_device_by_device_id(parent_device_id) + reg = parent_device.get_property('reg', None) + device_address = None + if reg is not None: + try: + # use always the first key to get first address inserted into dict + # because reg_index may be number or name + # reg//address/ : address + for reg_index in reg: + for address_index in reg[reg_index]['address']: + address = reg[reg_index]['address'][address_index] + device_address = hex(address)[2:].zfill(8) + break + break + except: + # this device is missing the register directive + pass + if device_address is None: + # use label instead of address + device_address = self.get_property('label', '') + # Warn about missing reg property + print("device.py: No register address property for device id '{}'." + .format(self._device_id), + "Using '{}' instead".format(device_address.lower())) + device_name += '_' + device_address + + device_name = device_name.replace("-", "_"). \ + replace(",", "_"). \ + replace(";", "_"). \ + replace("@", "_"). \ + replace("#", "_"). \ + replace("&", "_"). \ + replace("/", "_"). \ + lower() + return device_name + + ## + # @brief Get device tree property value of this device. + # + # @param property_path Path of the property to access + # (e.g. 'reg/0', 'interrupts/prio', 'device_id', ...) + # @return property value + # + def get_property(self, property_path, default=""): + property_value = self._edts['devices'][self._device_id] + property_path_elems = property_path.strip("'").split('/') + for elem_index, key in enumerate(property_path_elems): + if isinstance(property_value, dict): + property_value = property_value.get(key, None) + elif isinstance(property_value, list): + if int(key) < len(property_value): + property_value = property_value[int(key)] + else: + property_value = None + else: + property_value = None + if property_value is None: + if default == "": + default = "Device tree property {} not available in {}" \ + .format(property_path, self._device_id) + return default + return property_value + + def select_property(self, *args, **kwargs): + property_value = self._edts['devices'][self._device_id] + for arg in args: + arg = str(arg).strip("'") + if arg == "FIRST": + # take the first property + path_elem = list(property_value.keys())[0] + property_value = property_value[path_elem] + else: + for path_elem in arg.split('/'): + property_value = property_value[path_elem] + return property_value + + def get_properties(self): + return self._edts['devices'][self._device_id] + + def _properties_flattened(self, properties, path, flattened, path_prefix): + if isinstance(properties, dict): + for prop_name in properties: + super_path = "{}/{}".format(path, prop_name).strip('/') + self._properties_flattened(properties[prop_name], + super_path, flattened, + path_prefix) + elif isinstance(properties, list): + for i, prop in enumerate(properties): + super_path = "{}/{}".format(path, i).strip('/') + self._properties_flattened(prop, super_path, flattened, + path_prefix) + else: + flattened[path_prefix + path] = properties + + ## + # @brief Get the device tree properties flattened to property path : value. + # + # @param device_id + # @param path_prefix + # @return dictionary of property_path and property_value + def get_properties_flattened(self, path_prefix = ""): + flattened = dict() + self._properties_flattened(self.get_properties(), '', + flattened, path_prefix) + return flattened diff --git a/scripts/cogeno/cogeno/modules/edtsdb/devicetree/aliases.py b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/aliases.py new file mode 100644 index 000000000000..5711b29bc88b --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/aliases.py @@ -0,0 +1,24 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +## +# @brief DTS aliases +# +# Methods for aliases. +# +class DTSAliasesMixin(object): + __slots__ = [] + + def _init_aliases(self, root): + if 'children' in root: + if 'aliases' in root['children']: + for k, v in root['children']['aliases']['props'].items(): + self.aliases[v].append(k) + + # Treat alternate names as aliases + for k in self.reduced.keys(): + if self.reduced[k].get('alt_name', None) is not None: + self.aliases[k].append(self.reduced[k]['alt_name']) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/devicetree/chosen.py b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/chosen.py new file mode 100644 index 000000000000..ad622487b33b --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/chosen.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +## +# @brief DTS chosen +# +# Methods for chosen. +# +class DTSChosenMixin(object): + __slots__ = [] + + def _init_chosen(self, root): + if 'children' in root: + if 'chosen' in root['children']: + for k, v in root['children']['chosen']['props'].items(): + self.chosen[k] = v diff --git a/scripts/cogeno/cogeno/modules/edtsdb/devicetree/compatibles.py b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/compatibles.py new file mode 100644 index 000000000000..72b4cf4606db --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/compatibles.py @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +## +# @brief DTS compatibles +# +# Methods for compatibles. +# +class DTSCompatiblesMixin(object): + __slots__ = [] + + def _init_compatibles(self, d, name = '/'): + if 'props' in d: + compat = d['props'].get('compatible') + enabled = d['props'].get('status') + + if enabled == "disabled": + return + + if compat is not None: + self.compatibles[name] = compat + + if name != '/': + name += '/' + + if isinstance(d, dict): + if d['children']: + for k, v in d['children'].items(): + self._init_compatibles(v, name + k) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/devicetree/parser.py b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/parser.py new file mode 100644 index 000000000000..31d304dd9115 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/parser.py @@ -0,0 +1,260 @@ +# +# Copyright (c) 2017 Intel Corporation +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from collections import defaultdict +from pathlib import Path + +## +# @brief DTS parser +# +# Methods for device tree parsing. +# +class DTSParserMixin(object): + __slots__ = [] + + @staticmethod + def read_until(line, fd, end): + out = [line] + while True: + idx = line.find(end) + if idx < 0: + line = DTSParserMixin.clean_line(fd.readline(), fd) + out.append(line) + else: + out.append(line[idx + len(end):]) + return out + + @staticmethod + def remove_comment(line, fd): + out = [] + while True: + idx = line.find('/*') + if idx < 0: + idx = line.find('//') + if idx < 0: + out.append(line) + else: + out.append(line[:idx]) + return ' '.join(out) + + out.append(line[:idx]) + line = DTSParserMixin.read_until(line[idx:], fd, '*/')[-1] + + @staticmethod + def clean_line(line, fd): + return DTSParserMixin.remove_comment(line, fd).strip() + + @staticmethod + def _parse_node_name(line): + line = line[:-1] + + if '@' in line: + line, addr = line.split('@') + else: + addr = None + + if ':' in line: + if len(line.split(':')) == 3: + alt_label, label, name = line.split(':') + else: + label, name = line.split(':') + alt_label = None + else: + name = line + label = None + alt_label = None + + if addr is None: + return label, name.strip(), None, None, None + + if alt_label is None: + return label, name.strip(), addr, int(addr, 16), None + + return label, name.strip(), addr, int(addr, 16), alt_label + + @staticmethod + def _parse_values_internal(value, start, end, separator): + out = [] + + inside = False + accum = [] + for ch in value: + if not inside: + if ch == start: + inside = True + accum = [] + else: + if ch == end: + inside = False + out.append(''.join(accum)) + accum = [] + else: + accum.append(ch) + + if separator == ' ': + out = [v.split() for v in out] + + if len(out) == 1: + return DTSParserMixin._parse_value(out[0]) + + return [DTSParserMixin._parse_value(v) for v in out] + + @staticmethod + def _parse_values(value, start, end, separator): + out = DTSParserMixin._parse_values_internal(value, start, end, separator) + if isinstance(out, list) and \ + all(isinstance(v, str) and len(v) == 1 and not v.isalpha() for v in out): + return bytearray(out) + + return out + + @staticmethod + def _parse_value(value): + if value == '': + return value + + if isinstance(value, list): + out = [DTSParserMixin._parse_value(v) for v in value] + return out[0] if len(out) == 1 else out + + if value[0] == '<': + return DTSParserMixin._parse_values(value, '<', '>', ' ') + if value[0] == '"': + return DTSParserMixin._parse_values(value, '"', '"', ',') + if value[0] == '[': + return DTSParserMixin._parse_values(value, '[', ']', ' ') + + if value[0] == '&': + return {'ref': value[1:]} + + if value[0].isdigit(): + if value.startswith("0x"): + return int(value, 16) + if value[0] == '0': + return int(value, 8) + return int(value, 10) + + return value + + @staticmethod + def _parse_property(property, fd): + if '=' in property: + key, value = property.split('=', 1) + value = ' '.join(DTSParserMixin.read_until(value, fd, ';')).strip() + if not value.endswith(';'): + raise SyntaxError("parse_property: missing semicolon: %s" % value) + return key.strip(), DTSParserMixin._parse_value(value[:-1]) + + property = property.strip() + if not property.endswith(';'): + raise SyntaxError("parse_property: missing semicolon: %s" % property) + + return property[:-1].strip(), True + + @staticmethod + def _parse_build_node_name(name, addr): + if addr is None: + return name + elif isinstance(addr, int): + return '%s@%x' % (name, addr) + + return '%s@%s' % (name, addr.strip()) + + @staticmethod + def _parse_node(line, fd): + label, name, addr, numeric_addr, alt_label = DTSParserMixin._parse_node_name(line) + + node = { + 'label': label, + 'type': type, + 'addr': numeric_addr, + 'children': {}, + 'props': {}, + 'name': DTSParserMixin._parse_build_node_name(name, addr) + } + if alt_label: + node['alt_name'] = alt_label + + while True: + line = fd.readline() + if not line: + raise SyntaxError("parse_node: Missing } while parsing node") + + line = DTSParserMixin.clean_line(line, fd) + if not line: + continue + + if line == "};": + break + + if line.endswith('{'): + new_node = DTSParserMixin._parse_node(line, fd) + node['children'][new_node['name']] = new_node + else: + key, value = DTSParserMixin._parse_property(line, fd) + node['props'][key] = value + + return node + + ## + # Parse compiled DTS file + def _parse_file(self, fd, ignore_dts_version=False): + has_v1_tag = False + while True: + line = fd.readline() + if not line: + break + + line = DTSParserMixin.clean_line(line, fd) + if not line: + continue + + if line.startswith('/include/ '): + tag, filename = line.split() + with open(filename.strip()[1:-1], "r") as new_fd: + self._parse_file(new_fd, True) + elif line == '/dts-v1/;': + has_v1_tag = True + elif line.startswith('/memreserve/ ') and line.endswith(';'): + tag, start, end = line.split() + start = int(start, 16) + end = int(end[:-1], 16) + label = "reserved_memory_0x%x_0x%x" % (start, end) + self._dts[label] = { + 'type': 'memory', + 'reg': [start, end], + 'label': label, + 'addr': start, + 'name': DTSParserMixin._parse_build_node_name(name, start) + } + elif line.endswith('{'): + if not has_v1_tag and not ignore_dts_version: + raise SyntaxError("parse_file: Missing /dts-v1/ tag") + + new_node = DTSParserMixin._parse_node(line, fd) + self._dts[new_node['name']] = new_node + else: + raise SyntaxError("parse_file: Couldn't understand the line: %s" % line) + + def parse(self, dts_file_path): + self._dts = {} + self.compatibles = {} + self.phandles = {} + self.aliases = defaultdict(list) + self.chosen = {} + self.reduced = {} + + # load and parse DTS file + with Path(dts_file_path).open(mode="r", encoding="utf-8") as dts_fd: + self._parse_file(dts_fd) + + # build up useful lists - reduced first as it used by the build of others + self._init_reduced(self._dts['/']) + self._init_phandles(self._dts['/']) + self._init_aliases(self._dts['/']) + self._init_chosen(self._dts['/']) + self._init_compatibles(self._dts['/']) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/devicetree/phandles.py b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/phandles.py new file mode 100644 index 000000000000..6af40752222f --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/phandles.py @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +## +# @brief DTS phandles +# +# Methods for phandles. +# +class DTSPHandlesMixin(object): + __slots__ = [] + + def _init_phandles(self, root, name = '/'): + if 'props' in root: + handle = root['props'].get('phandle') + enabled = root['props'].get('status') + + if enabled == "disabled": + return + + if handle is not None: + self.phandles[handle] = name + + if name != '/': + name += '/' + + if isinstance(root, dict): + if root['children']: + for k, v in root['children'].items(): + self._init_phandles(v, name + k) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/devicetree/reduced.py b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/reduced.py new file mode 100644 index 000000000000..8c680c2ea128 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/devicetree/reduced.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +## +# @brief DTS reduced +# +# Methods for reduced. +# +class DTSReducedMixin(object): + __slots__ = [] + + def _init_reduced(self, nodes, path = '/'): + # compress nodes list to nodes w/ paths, add interrupt parent + if 'props' in nodes: + status = nodes['props'].get('status') + + if status == "disabled": + return + + if isinstance(nodes, dict): + self.reduced[path] = dict(nodes) + self.reduced[path].pop('children', None) + if path != '/': + path += '/' + if nodes['children']: + for k, v in nodes['children'].items(): + self._init_reduced(v, path + k) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/dts.py b/scripts/cogeno/cogeno/modules/edtsdb/dts.py new file mode 100644 index 000000000000..8dc9101dc298 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/dts.py @@ -0,0 +1,188 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +import sys +from pathlib import Path +from collections import defaultdict +from copy import deepcopy + +from .devicetree.phandles import DTSPHandlesMixin +from .devicetree.compatibles import DTSCompatiblesMixin +from .devicetree.aliases import DTSAliasesMixin +from .devicetree.chosen import DTSChosenMixin +from .devicetree.reduced import DTSReducedMixin +from .devicetree.parser import DTSParserMixin + +## +# class that holds the information that was read out +# of the device tree specification +# +class DTS(DTSPHandlesMixin, DTSCompatiblesMixin, DTSAliasesMixin, + DTSChosenMixin, DTSReducedMixin, DTSParserMixin): + + regs_config = { + 'zephyr,flash' : 'CONFIG_FLASH', + 'zephyr,sram' : 'CONFIG_SRAM', + 'zephyr,ccm' : 'CONFIG_CCM' + } + + name_config = { + 'zephyr,console' : 'CONFIG_UART_CONSOLE_ON_DEV_NAME', + 'zephyr,bt-uart' : 'CONFIG_BT_UART_ON_DEV_NAME', + 'zephyr,uart-pipe' : 'CONFIG_UART_PIPE_ON_DEV_NAME', + 'zephyr,bt-mon-uart' : 'CONFIG_BT_MONITOR_ON_DEV_NAME', + 'zephyr,uart-mcumgr' : 'CONFIG_UART_MCUMGR_ON_DEV_NAME' + } + + @staticmethod + def find_node_by_path(nodes, path): + d = nodes + for k in path[1:].split('/'): + d = d['children'][k] + + return d + + @staticmethod + def get_parent_address(node_address): + parent_address = '' + + for comp in node_address.split('/')[1:-1]: + parent_address += '/' + comp + + return parent_address + + @staticmethod + def node_top_address(node_address): + address = node_address.split('/')[1] + return address + + def __init__(self, dts_file_path = None): + self._dts = {} + self.compatibles = {} + self.phandles = {} + self.aliases = defaultdict(list) + self.chosen = {} + self.reduced = {} + # load and parse DTS file + if dts_file_path is not None: + self.parse(dts_file_path) + + def find_parent_prop(self, node_address, prop): + parent_address = get_parent_address(node_address) + + if prop in self.reduced[parent_address]['props']: + parent_prop = self.reduced[parent_address]['props'].get(prop) + else: + raise Exception("Parent of node " + node_address + + " has no " + prop + " property") + + return parent_prop + + def get_node_compat(self, node_address): + compat = None + + if 'props' in self.reduced[node_address]: + compat = self.reduced[node_address]['props'].get('compatible', None) + if isinstance(compat, list): + compat = compat[0] + + return compat + + def get_parent_compat(self, node_address): + parent_address = self.get_parent_address(node_address) + if not (parent_address in self.reduced): + # no parent - check whether there should be one + if parent_address.count('/') > 1: + # there should be a parent, maybe it is not activated + raise Exception("Parent '{}' of node '{}' is not activated." + .format(parent_address, node_address)) + return None + + return self.get_node_compat(parent_address) + + + def get_compats(self, node_address): + compat = None + + if 'props' in self.reduced[node_address]: + compat = self.reduced[node_address]['props'].get('compatible', None) + if compat and not isinstance(compat, list): + compat = [compat, ] + + return compat + + def get_compat(self, node_address): + compat = None + + compat = self.get_node_compat(node_address) + + if compat == None: + compat = self.get_parent_compat(node_address) + + return compat + + # Get the #{address,size}-cells for a given node + def get_addr_size_cells(self, node_address): + parent_addr = self.get_parent_address(node_address) + if parent_addr == '': + parent_addr = '/' + + # The DT spec says that if #address-cells is missing default to 2 + # if #size-cells is missing default to 1 + nr_addr = self.reduced[parent_addr]['props'].get('#address-cells', 2) + nr_size = self.reduced[parent_addr]['props'].get('#size-cells', 1) + + return (nr_addr, nr_size) + + def translate_addr(self, addr, node_address, nr_addr_cells, nr_size_cells): + + try: + ranges = deepcopy(self.find_parent_prop(node_address, 'ranges')) + if type(ranges) is not list: ranges = [ ] + except: + return 0 + + parent_address = self.get_parent_address(node_address) + + (nr_p_addr_cells, nr_p_size_cells) = self.get_addr_size_cells(parent_address) + + 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 = self.translate_addr(addr + range_offset, + parent_address, nr_p_addr_cells, nr_p_size_cells) + range_offset += parent_range_offset + + return range_offset + + def get_controller_type(self, node_address): + controller_type = '' + controller_props = self.reduced[node_address]['props'] + for prop in controller_props: + if 'controller' in prop: + controller_type = prop + break + if 'parent' in prop: + controller_type = prop + break + if controller_type == '': + raise Exception("Try to instantiate {} as a controller".format(node_address)) + return controller_type diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractor.py b/scripts/cogeno/cogeno/modules/edtsdb/extractor.py new file mode 100644 index 000000000000..e4b47cf9d8ab --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractor.py @@ -0,0 +1,139 @@ +# +# Copyright (c) 2017, Linaro Limited +# Copyright (c) 2018, Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +import re +from copy import deepcopy + +from .binder import Binder +from .dts import DTS +from .extractors.extractors import Extractors + +## +# @brief ETDS Database extractor +# +# Methods for ETDS database extraction from DTS. +# +class EDTSExtractorMixin(object): + __slots__ = [] + + def extractors(self): + return self._extractors + + def _extract_property(self, node_address, prop): + extractors = self.extractors() + + if prop == 'reg': + if 'partition@' in node_address: + # reg in partition is covered by flash handling + extractors.extract_flash(node_address, prop) + else: + extractors.extract_reg(node_address, prop) + elif prop == 'interrupts' or prop == 'interrupts-extended': + extractors.extract_interrupts(node_address, prop) + elif prop == 'compatible': + extractors.extract_compatible(node_address, prop) + elif '-controller' in prop: + extractors.extract_controller(node_address, prop) + elif 'pinctrl-' in prop: + extractors.extract_pinctrl(node_address, prop) + elif prop.startswith(('gpio', )) or 'gpios' in prop: + extractors.extract_gpio(node_address, prop) + elif prop.startswith(('clock', '#clock', 'assigned-clock', 'oscillator')): + extractors.extract_clocks(node_address, prop) + elif prop.startswith(('dma', '#dma')): + extractors.extract_dma(node_address, prop) + elif prop.startswith(('pwm', '#pwm')): + extractors.extract_pwm(node_address, prop) + else: + extractors.extract_default(node_address, prop) + + def _extract_node(self, root_node_address, sub_node_address, + sub_node_binding): + dts = self.extractors().dts() + bindings = self.extractors().bindings() + extractors = self.extractors() + + node = dts.reduced[sub_node_address] + node_compat = dts.get_compat(root_node_address) + + if node_compat not in bindings: + return + + if sub_node_binding is None: + node_binding = bindings[node_compat] + else: + node_binding = sub_node_binding + + # Do extra property definition based on heuristics + extractors.extract_heuristics(sub_node_address, None) + + # check to see if we need to process the properties + for binding_prop, binding_prop_value in node_binding['properties'].items(): + if 'properties' in binding_prop_value: + for node_address in dts.reduced: + if root_node_address + '/' in node_address: + # subnode detected + self._extract_node(root_node_address, node_address, + binding_prop_value) + # We ignore 'generation' as we dot not create defines + # and extract all properties that are: + # - not marked by the extractors to be ignored and are + # - in the binding + for prop in node['props']: + # if prop is in filter list - ignore it + if prop in extractors.ignore_properties: + continue + + if re.match(binding_prop + '$', prop): + self._extract_property(sub_node_address, prop) + + def _extract_specification(self): + dts = self.extractors().dts() + bindings = self.extractors().bindings() + extractors = self.extractors() + + # extract node info for devices + for node_address in dts.reduced: + node_compat = dts.get_compat(node_address) + # node or parent node has a compat, it's a device + if node_compat is not None and node_compat in bindings: + self._extract_node(node_address, node_address, None) + + # extract chosen node info + for k, v in dts.regs_config.items(): + if k in dts.chosen: + extractors.default.edts_insert_chosen(k, dts.chosen[k]) + #extractors.extract_reg(dts.chosen[k], None, None, v, 1024) + for k, v in dts.name_config.items(): + if k in dts.chosen: + extractors.default.edts_insert_chosen(k, dts.chosen[k]) + + node_address = dts.chosen.get('zephyr,flash', 'dummy-flash') + extractors.extract_flash(node_address, 'zephyr,flash') + node_address = dts.chosen.get('zephyr,code-partition', node_address) + extractors.extract_flash(node_address, 'zephyr,code-partition') + + ## + # @brief Extract DTS info to database + # + # @param dts_file_path DTS file + # @param bindings_paths YAML file directories, we allow multiple + # + def extract(self, dts_file_path, bindings_paths): + + dts = DTS(dts_file_path) + + # Extract bindings of all compatibles + bindings = Binder.bindings(dts.compatibles, bindings_paths) + if bindings == {}: + raise Exception("Missing Bindings information. Check YAML sources") + + # Create extractors + self._extractors = Extractors(self, dts, bindings) + + # Extract device tree specification + self._extract_specification() diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/clocks.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/clocks.py new file mode 100644 index 000000000000..b055ec92f9c4 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/clocks.py @@ -0,0 +1,330 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage clocks related directives. +# +# Handles: +# - clocks +# - clock-names +# - clock-output-names +# - clock-indices +# - clock-ranges +# - clock-frequency +# - clock-accuracy +# - oscillator +# - assigned-clocks +# - assigned-clock-parents +# - assigned-clock-rates +# +# Generates in EDTS: +# +# Clock provider +# -------------- +# - clock-output//name : clock output name +# - clock-output//clock-frequency : fixed clock output frequency in Hz +# - clock-output//clock-accuracy : accuracy of clock in ppb (parts per billion). +# - clock-output//oscillator : True +# +# Clock consumer +# -------------- +# - clocks//name : name of clock input +# - clocks//provider : device id of clock provider +# - clocks//clock-ranges : True +# - clocks// : +# (cell-name from cell-names of provider) +# - assigned-clocks//provider : device id of provider of assigned clock +# - assigned-clocks//rate : selected rate of assigned clock in Hz +# - assigned-clocks// : +# (cell-name from cell-names of provider) +# - assigned-clocks//parent/provider : provider of parent clock of assigned clock +# - assigned-clocks//parent/ : +# (cell-name from cell-names of provider) +# +# Other nodes +# ----------- +# - clock-frequency : clock frequency in Hz +# +class DTClocks(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + # Ignore properties that are covered by the extraction of + extractors.ignore_properties.extend([ \ + # - 'clocks' for clock consumers + 'clock-names', 'clock-ranges', + # - '#clock-cells' for clock providers + # ('clock-frequency' is an exemption) + 'clock-output-names', 'clock-indices', + 'clock-accuracy', 'oscillator', + # - 'assigned-clocks' for clock consumer assigned clocks + 'assigned-clock-parents', 'assigned-clock-rates']) + ## + # Dictionary of all clocks + # primary key is the top node + # secondary key is is the clock name, + # value is the clock id + self._clocks = {} + + ## + # @brief Get output clock names of clock provider + # + def _clock_output_names(self, clock_provider): + names = clock_provider['props'].get('clock-output-names', []) + if len(names) == 0: + names = [clock_provider['props'].get('label', '')] + elif not isinstance(names, list): + names = [names] + return names + + ## + # @brief Get clock id of output clock of clock provider + # + # @param clock_provider + # @param clock_output_name + # @return clock id + # + def _clock_output_id(self, clock_provider, clock_output_name): + clock_output_names = self._clock_output_names(clock_provider) + clock_indices = clock_provider['props'].get('clock-indices', None) + if clock_indices: + if len(clock_output_names) != len(clock_indices): + raise Exception( + ("clock-output-names count ({}) does not match" + " clock-indices count ({}) in clock provider {}.") + .format(len(clock_output_names), len(clock_indices), + str(clock_provider['name']))) + for i, name in enumerate(clock_output_names): + if name == clock_output_name: + if clock_indices: + return clock_indices[i] + return i + return None + + ## + # @brief Get clock name of output clock of clock provider + # + # @param clock_provider + # @param clock_output_name + # @return clock id + # + def _clock_output_name(self, clock_provider, clock_output_id): + clock_output_names = self._clock_output_names(clock_provider) + clock_indices = clock_provider['props'].get('clock-indices', None) + if clock_indices: + for i, clock_id in enumerate(clock_indices): + if clock_id == clock_output_id: + clock_output_id = i + break + return clock_output_names[clock_output_id] + + ## + # @brief Get clock name of input clock of clock consumer + # + def _clock_input_name(self, clock_consumer, clock_input_index): + clock_names = clock_consumer['props'].get('clock-names', None) + if clock_names is None: + return "{}".format(clock_input_index) + if not isinstance(clock_names, list): + clock_names = [clock_names] + if len(clock_names) <= clock_input_index: + return "{}".format(clock_input_index) + return clock_names[clock_input_index] + + ## + # @brief Insert clock cells into EDTS + # + # @param clock_consumer_node_address + # @param clock_provider_node_address + # @param clock_cells + # @param property_path_templ "xxx/yyy/{}" + # + def edts_insert_clock_cells(self, clock_consumer_node_address, + clock_provider_node_address, clock_cells, + property_path_templ): + if len(clock_cells) == 0: + return + clock_consumer_device_id = self.edts_device_id(clock_consumer_node_address) + clock_provider_device_id = self.edts_device_id(clock_provider_node_address) + clock_provider = self.dts().reduced[clock_provider_node_address] + clock_provider_compat = self.dts().get_compat(clock_provider_node_address) + clock_provider_binding = self.bindings()[clock_provider_compat] + clock_cells_names = clock_provider_binding.get('#cells', ['ID', ]) + cells_list = [] + for i, cell in enumerate(clock_cells): + if i >= len(clock_cells_names): + clock_cell_name = 'CELL{}'.format(i).lower() + else: + clock_cell_name = clock_cells_names[i].lower() + self.edts().insert_device_property(clock_consumer_device_id, + property_path_templ.format(clock_cell_name), cell) + cells_list.append(cell) + self.edts_insert_device_controller(clock_provider_node_address, + clock_consumer_node_address, + cells_list) + + + def _extract_consumer(self, node_address, clocks): + + clock_consumer_device_id = self.edts_device_id(node_address) + clock_consumer = self.dts().reduced[node_address] + + clock_index = 0 + clock_cell_index = 0 + clock_nr_cells = 0 + clock_provider_node_address = '' + clock_provider = {} + for cell in clocks: + if clock_cell_index == 0: + if cell not in self.dts().phandles: + raise Exception( + ("Could not find the clock provider node {} for clocks" + " = {} in clock consumer node {}. Did you activate" + " the clock node?. Last clock provider: {}.") + .format(str(cell), str(clocks), node_address, + str(clock_provider))) + clock_provider_node_address = self.dts().phandles[cell] + clock_provider = self.dts().reduced[clock_provider_node_address] + clock_nr_cells = int(clock_provider['props'].get('#clock-cells', 0)) + clock_cells = [] + else: + clock_cells.append(cell) + clock_cell_index += 1 + if clock_cell_index > clock_nr_cells: + # generate EDTS + clock_name = self._clock_input_name(clock_consumer, clock_index) + self.edts().insert_device_property(clock_consumer_device_id, + 'clocks/{}/provider'.format(clock_name), + self.edts_device_id(clock_provider_node_address)) + for prop in ('clock-ranges',): + value = clock_consumer['props'].get(prop, None) + if value is not None: + self.edts().insert_device_property(clock_consumer_device_id, + 'clocks/{}/{}'.format(clock_name, prop), value) + self.edts_insert_clock_cells(node_address, + clock_provider_node_address, clock_cells, + "clocks/{}/{{}}".format(clock_name)) + + clock_cell_index = 0 + clock_index += 1 + + def _extract_assigned(self, node_address, prop_list): + + clock_consumer = self.dts().reduced[node_address] + clocks = clock_consumer['props'].get('assigned-clocks', None) + clock_parents = clock_consumer['props'].get('assigned-clock-parents', None) + clock_rates = clock_consumer['props'].get('assigned-clock-rates', None) + + # generate EDTS + clock_consumer_device_id = self.edts_device_id(node_address) + clock_index = 0 + clock_parents_index = 0 + clock_cell_index = 0 + clock_nr_cells = 1 # [phandle, clock specifier] -> 1 specifier + clock_provider_node_address = '' + clock_provider = {} + for cell in clocks: + if clock_cell_index == 0: + if cell not in self.dts().phandles: + raise Exception( + ("Could not find the clock provider node {} for assigned-clocks" + " = {} in clock consumer node {}. Did you activate" + " the clock node?. Last clock provider: {}.") + .format(str(cell), str(clocks), node_address, + str(clock_provider))) + clock_provider_node_address = self.dts().phandles[cell] + clock_provider = self.dts().reduced[clock_provider_node_address] + clock_nr_cells = int(clock_provider['props'].get('#clock-cells', clock_nr_cells)) + clock_cells = [] + else: + clock_cells.append(cell) + clock_cell_index += 1 + if clock_cell_index > clock_nr_cells: + # - assigned clock provider + self.edts().insert_device_property(clock_consumer_device_id, + 'assigned-clock/{}/provider'.format(clock_index), + self.edts_device_id(clock_provider_node_address)) + # - assigned clock provider output index + self.edts_insert_clock_cells(node_address, + clock_provider_node_address, clock_cells, + 'assigned-clock/{}/\{\}'.format(clock_index)) + # - assigned clock rate + if len(clock_rates) > clock_index: + self.edts().insert_device_property(clock_consumer_device_id, + 'assigned-clock/{}/rate'.format(clock_index), + clock_rates[clock_index]) + # - assigned clock parent + if len(clock_parents) > clock_parents_index + 1: + clock_parent_node_address = self.dts().phandles[clock_parents[clock_parents_index]] + clock_parent = self.dts().reduced[clock_parent_node_address] + clock_parent_nr_cells = int(clock_parent['props'].get('#clock-cells', 0)) + clock_parent_cells = clock_parents[ + clock_parents_index + 1 : + clock_parents_index + 1 + clock_parent_nr_cells] + self.edts().insert_device_property(clock_consumer_device_id, + 'assigned-clock/{}/parent/provider'.format(clock_index), + self.edts_device_id(clock_parent_node_address)) + self.edts_insert_clock_cells(node_address, + clock_parent_node_address, clock_parent_cells, + 'assigned-clock/{}/parent/\{\}'.format(clock_index)) + clock_parents_index += clock_parent_nr_cells + 1 + + clock_cell_index = 0 + clock_index += 1 + + def _extract_provider(self, node_address, prop_list): + + clock_provider = self.dts().reduced[node_address] + + # generate EDTS + clock_provider_device_id = self.edts_device_id(node_address) + for output_name in self._clock_output_names(clock_provider): + output_id = self._clock_output_id(clock_provider, output_name) + self.edts().insert_device_property(clock_provider_device_id, + 'output-clock/{}/name'.format(output_id), output_name) + for prop in ('clock-frequency', 'clock-accuracy', 'oscillator'): + value = clock_provider['props'].get(prop, None) + if value is not None: + self.edts().insert_device_property(clock_provider_device_id, + 'output-clock/{}/{}'.format(output_id, prop), value) + + ## + # @brief Extract clocks related directives + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + properties = self.dts().reduced[node_address]['props'][prop] + + prop_list = [] + if not isinstance(properties, list): + prop_list.append(properties) + else: + prop_list = list(properties) + + if prop == 'clocks': + # indicator for clock consumers + self._extract_consumer(node_address, prop_list) + elif prop == '#clock-cells': + # indicator for clock providers + self._extract_provider(node_address, prop_list) + elif prop in 'assigned-clocks': + # indicator for assigned clocks + self._extract_assigned(node_address, prop_list) + elif prop == 'clock-frequency': + # clock-frequency is used by nodes besides the + # clock provider nodes (which is covered by '#clock-cells'. + if not '#clock-cells' in self.dts().reduced[node_address]['props']: + self.edts_insert_device_property(node_address, prop, + prop_list[0]) + else: + raise Exception( + "DTClocks.extract called with unexpected directive ({})." + .format(prop)) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/compatible.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/compatible.py new file mode 100644 index 000000000000..029bc7648022 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/compatible.py @@ -0,0 +1,40 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage compatible directives. +# +# Handles: +# - compatible +# +# Generates in EDTS: +# - compatible/ : compatible +class DTCompatible(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + + ## + # @brief Extract compatible + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + # compatible definition + compatible = self.dts().reduced[node_address]['props'][prop] + if not isinstance(compatible, list): + compatible = [compatible, ] + + # generate EDTS + device_id = self.edts_device_id(node_address) + for i, comp in enumerate(compatible): + self.edts().insert_device_property(device_id, + 'compatible/{}'.format(i), + comp) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/controller.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/controller.py new file mode 100644 index 000000000000..0c3abc6759bb --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/controller.py @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage -controller directives. +# +# Handles: +# - -controller +# +# Generates in EDTS: +# - -controller : True +class DTController(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + + ## + # @brief Extract -controller + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + # generate EDTS + device_id = self.edts_device_id(node_address) + self.edts().insert_device_property(device_id, prop, True) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/default.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/default.py new file mode 100644 index 000000000000..ab555177bd18 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/default.py @@ -0,0 +1,31 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage directives in a default way. +# +class DTDefault(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + + ## + # @brief Extract directive information. + # + # @param edts Extended device tree database + # @param dts Device tree specification + # @param bindings Device tree bindings + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + prop_values = self.dts().reduced[node_address]['props'][prop] + + # generate EDTS + self.edts_insert_device_property(node_address, prop, prop_values) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/directive.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/directive.py new file mode 100644 index 000000000000..2e0ec1e13e22 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/directive.py @@ -0,0 +1,109 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from ..dts import * +from ..binder import * + +## +# @brief Base class for pool of extractors for device tree data extraction. +# +class ExtractorsBase(object): + + def __init__(self, edts, dts, bindings): + self._edts = edts + self._dts = dts + self._bindings = bindings + # Extractors + self.clocks = None + self.compatible = None + self.controller = None + self.default = None + self.dma = None + self.flash = None + self.gpioranges = None + self.gpios = None + self.heuristics = None + self.interrupts = None + self.pinctrl = None + self.pwm = None + self.reg = None + ## + # Properties that the extractors want to be ignored + # because they are unnecessary or are covered by the + # extraction of other properties + self.ignore_properties = [] + + # convenience functions + + def dts(self): + return self._dts + + def edts(self): + return self._edts + + def bindings(self): + return self._bindings + +## +# @brief Base class for device tree directives extraction to edts. +# +class DTDirective(object): + + def __init__(self, extractors): + ## The extractor pool this extractor belongs to + self._extractors = extractors + + def dts(self): + return self._extractors._dts + + def edts(self): + return self._extractors._edts + + def bindings(self): + return self._extractors._bindings + + def extractors(self): + return self._extractors + + ## + # @brief Get EDTS device id associated to node address. + # + # @return ETDS device id + def edts_device_id(self, node_address): + return node_address + + + def edts_insert_chosen(self, chosen, node_address): + self.edts().insert_chosen(chosen, self.edts_device_id(node_address)) + + ## + # @brief Insert device property into EDTS + # + def edts_insert_device_property(self, node_address, property_path, + property_value): + + device_id = self.edts_device_id(node_address) + self.edts().insert_device_property(device_id, property_path, property_value) + + ## + # @brief Insert device controller into EDTS + # + def edts_insert_device_controller(self, controller_address, node_address, + line): + device_id = self.edts_device_id(node_address) + controller_id = self.edts_device_id(controller_address) + controller_type = self.dts().get_controller_type(controller_address) + self.edts().insert_device_controller(controller_type, controller_id, + device_id, line) + + ## + # @brief Extract directive information. + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + pass diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/dma.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/dma.py new file mode 100644 index 000000000000..62639f8735f6 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/dma.py @@ -0,0 +1,158 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage dma directives. +# +# Handles: +# - dma-channels +# - dma-requests +# - dmas +# - dma-names +# +# Generates in EDTS: +# +# DMA controller +# -------------- +# - dma-channels : Number of DMA channels supported by the controller. +# - dma-requests : Number of DMA request signals supported by the controller +# +# DMA client +# ---------- +# - TBD +# +class DTDMA(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + # Ignore properties that are covered by the extraction of + extractors.ignore_properties.extend([ \ + # - 'dmas' for dma clients + 'dma-names', + # '- #dma-cells' for dma controllers + 'dma-channels', 'dma-requests']) + + ## + # @brief Insert dma cells into EDTS + # + # @param dma_client + # @param dma_controller + # @param dma_cells + # @param property_path_templ "xxx/yyy/{}" + # + def edts_insert_dma_cells(self, dma_client_node_address, + dma_controller_node_address, dma_cells, + property_path_templ): + dts = self.dts() + bindings = self.bindings() + edts = self.edts() + + if len(dma_cells) == 0: + return + dma_client_device_id = self.edts_device_id(dma_client_node_address) + dma_controller_device_id = self.edts_device_id(dma_controller_node_address) + dma_controller = dts.reduced[dma_controller_node_address] + dma_controller_compat = dts.get_compat(dma_controller_node_address) + dma_controller_bindings = bindings[dma_controller_compat] + dma_nr_cells = int(dma_controller['props'].get('#dma-cells', 0)) + dma_cells_names = dma_controller_bindings.get( + '#dma-cells', ['ID', 'CELL1', "CELL2", "CELL3"]) + for i, cell in enumerate(dma_cells): + if i >= len(dma_cells_names): + dma_cell_name = 'CELL{}'.format(i).lower() + else: + dma_cell_name = dma_cells_names[i].lower() + edts.insert_device_property(dma_client_device_id, + property_path_templ.format(dma_cell_name), cell) + + def _extract_client(self, node_address, dmas): + dts = self.dts() + edts = self.edts() + + dma_client_device_id = self.edts_device_id(node_address) + dma_client = dts.reduced[node_address] + dma_client_compat = dts.get_compat(node_address) + dma_client_dma_names = dma_client['props'].get('dma-names', None) + + dma_index = 0 + dma_cell_index = 0 + dma_nr_cells = 0 + dma_controller_node_address = '' + dma_controller = {} + for cell in dmas: + if dma_cell_index == 0: + if cell not in dts.phandles: + raise Exception( + ("Could not find the dma controller node {} for dmas" + " = {} in dma client node {}. Did you activate" + " the dma node?. Last dma controller: {}.") + .format(str(cell), str(dmas), node_address, + str(dma_controller))) + dma_controller_node_address = dts.phandles[cell] + dma_controller = dts.reduced[dma_controller_node_address] + dma_nr_cells = int(dma_controller['props'].get('#dma-cells', 1)) + dma_cells = [] + else: + dma_cells.append(cell) + dma_cell_index += 1 + if dma_cell_index > dma_nr_cells: + if len(dma_client_dma_names) > dma_index: + dma_name = dma_client_dma_names[dma_index] + else: + dma_name = str(dma_index) + # generate EDTS + edts.insert_device_property(dma_client_device_id, + 'dmas/{}/controller'.format(dma_name), + self.edts_device_id(dma_controller_node_address)) + self.edts_insert_dma_cells(node_address, + dma_controller_node_address, + dma_cells, + "dmas/{}/{{}}".format(dma_name)) + + dma_cell_index = 0 + dma_index += 1 + + def _extract_controller(self, node_address, prop_list): + dts = self.dts() + edts = self.edts() + + dma_controller = dts.reduced[node_address] + + # generate EDTS + dma_controller_device_id = self.edts_device_id(node_address) + edts.insert_device_property(dma_controller_device_id, + 'dma-channels', dma_controller['props']['dma-channels']) + edts.insert_device_property(dma_controller_device_id, + 'dma-requests', dma_controller['props']['dma-requests']) + + ## + # @brief Extract dma related directives + # + # @param node_address Address of node owning the dmaxxx definition. + # @param prop dmaxxx property name + # + def extract(self, node_address, prop): + + properties = self.dts().reduced[node_address]['props'][prop] + + prop_list = [] + if not isinstance(properties, list): + prop_list.append(properties) + else: + prop_list = list(properties) + + if prop == 'dmas': + # indicator for dma clients + self._extract_client(node_address, prop_list) + elif prop == '#dma-cells': + # indicator for dma controllers + self._extract_controller(node_address, prop_list) + else: + raise Exception( + "DTDMA.extract called with unexpected directive ({})." + .format(prop)) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/extractors.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/extractors.py new file mode 100644 index 000000000000..454a939cd82d --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/extractors.py @@ -0,0 +1,78 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import ExtractorsBase +from .clocks import DTClocks +from .compatible import DTCompatible +from .controller import DTController +from .default import DTDefault +from .dma import DTDMA +from .flash import DTFlash +from .gpio import DTGpio +from .heuristics import DTHeuristics +from .interrupts import DTInterrupts +from .pinctrl import DTPinCtrl +from .pwm import DTPWM +from .reg import DTReg + +## +# @brief Pool of extractors for device tree data extraction. +# +class Extractors(ExtractorsBase): + + def __init__(self, edts, dts, bindings): + super().__init__(edts, dts, bindings) + # init all extractors + self.clocks = DTClocks(self) + self.compatible = DTCompatible(self) + self.controller = DTController(self) + self.default = DTDefault(self) + self.dma = DTDMA(self) + self.flash = DTFlash(self) + self.gpio = DTGpio(self) + self.heuristics = DTHeuristics(self) + self.interrupts = DTInterrupts(self) + self.pinctrl = DTPinCtrl(self) + self.pwm = DTPWM(self) + self.reg = DTReg(self) + + # convenience functions + + def extract_clocks(self, node_address, prop): + self.clocks.extract(node_address, prop) + + def extract_compatible(self, node_address, prop): + self.compatible.extract(node_address, prop) + + def extract_controller(self, node_address, prop): + self.controller.extract(node_address, prop) + + def extract_default(self, node_address, prop): + self.default.extract(node_address, prop) + + def extract_dma(self, node_address, prop): + self.dma.extract(node_address, prop) + + def extract_flash(self, node_address, prop): + self.flash.extract(node_address, prop) + + def extract_gpio(self, node_address, prop): + self.gpio.extract(node_address, prop) + + def extract_heuristics(self, node_address, prop): + self.heuristics.extract(node_address, prop) + + def extract_interrupts(self, node_address, prop): + self.interrupts.extract(node_address, prop) + + def extract_pinctrl(self, node_address, prop): + self.pinctrl.extract(node_address, prop) + + def extract_pwm(self, node_address, prop): + self.pwm.extract(node_address, prop) + + def extract_reg(self, node_address, prop): + self.reg.extract(node_address, prop) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/flash.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/flash.py new file mode 100644 index 000000000000..990da46c748a --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/flash.py @@ -0,0 +1,105 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage flash directives. +# +class DTFlash(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + # Node of the flash + self._flash_node = None + + def _extract_partition(self, node_address, prop): + dts = self.dts() + + node = dts.reduced[node_address] + partition_name = node['props']['label'] + partition_sectors = node['props']['reg'] + + nr_address_cells = dts.reduced['/']['props'].get('#address-cells') + nr_size_cells = dts.reduced['/']['props'].get('#size-cells') + address = '' + for comp in node_address.split('/')[1:-1]: + address += '/' + comp + nr_address_cells = dts.reduced[address]['props'].get( + '#address-cells', nr_address_cells) + nr_size_cells = dts.reduced[address]['props'].get('#size-cells', nr_size_cells) + + # generate EDTS + sector_index = 0 + sector_cell_index = 0 + sector_nr_cells = nr_address_cells + nr_size_cells + for cell in partition_sectors: + if sector_cell_index < nr_address_cells: + self.edts_insert_device_property(node_address, + 'sector/{}/offset/{}'.format(sector_index, sector_cell_index), + cell) + else: + size_cell_index = sector_cell_index - nr_address_cells + self.edts_insert_device_property(node_address, + 'sector/{}/size/{}'.format(sector_index, size_cell_index), + cell) + sector_cell_index += 1 + if sector_cell_index >= sector_nr_cells: + sector_cell_index = 0 + sector_index += 1 + + def _extract_flash(self, node_address, prop): + dts = self.dts() + edts = self.edts() + extractors = self.extractors() + + device_id = self.edts_device_id(node_address) + + if node_address == 'dummy-flash': + # We will add addr/size of 0 for systems with no flash controller + # This is what they already do in the Kconfig options anyway + edts.insert_device_property(device_id, 'reg/0/address/0', "0") + edts.insert_device_property(device_id, 'reg/0/size/0', "0") + self._flash_node = None + self._flash_base_address = 0 + else: + self._flash_node = dts.reduced[node_address] + + flash_props = ["label", "write-block-size", "erase-block-size"] + for prop in flash_props: + if prop in self._flash_node['props']: + extractors.default.extract(node_address, prop,) + + def _extract_code_partition(self, node_address, prop): + # do sanity check + if node_address != 'dummy-flash': + if self._flash_node is None: + # No flash node scanned before- + raise Exception( + "Code partition '{}' {} without flash definition." + .format(prop, node_address)) + + ## + # @brief Extract flash + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + if prop == 'zephyr,flash': + # indicator for flash + self._extract_flash(node_address, prop) + elif prop == 'zephyr,code-partition': + # indicator for code_partition + self._extract_code_partition(node_address, prop) + elif prop == 'reg': + # indicator for partition + self._extract_partition(node_address, prop) + else: + raise Exception( + "DTFlash.extract called with unexpected directive ({})." + .format(prop)) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/gpio.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/gpio.py new file mode 100644 index 000000000000..0154fcebc063 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/gpio.py @@ -0,0 +1,172 @@ +# +# Copyright (c) 2017 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage gpio related directive. +# +# Handles: +# - gpios +# - gpio-ranges +# +# Generates in EDTS: +# - gpios//controller : device_id of gpio controller +# - gpios// : +# (cell-name from cell-names of gpio controller) +# - gpio-ranges//base : base pin of gpio +# - gpio-ranges//npins : number of pins +# - gpio-ranges//pin-controller : device_id of pin controller +# - gpio-ranges//pin-controller-base : base pin of pin controller +# +class DTGpio(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + + ## + # @brief Insert gpio cells into EDTS + # + # @param gpio_client_node_address + # @param gpio_node_address + # @param gpio_cells + # @param property_path_templ "xxx/yyy/{}" + # + def edts_insert_gpio_cells(self, gpio_client_node_address, + gpio_node_address, gpio_cells, + property_path_templ): + if len(gpio_cells) == 0: + return + gpio_client_device_id = self.edts_device_id(gpio_client_node_address) + gpio_device_id = self.edts_device_id(gpio_node_address) + gpio = self.dts().reduced[gpio_node_address] + gpio_compat = self.dts().get_compat(gpio_node_address) + gpio_binding = self.bindings()[gpio_compat] + gpio_cells_names = gpio_binding.get('#cells', ['ID', ]) + cells_list = [] + for i, cell in enumerate(gpio_cells): + if i >= len(gpio_cells_names): + gpio_cell_name = 'CELL{}'.format(i).lower() + else: + gpio_cell_name = gpio_cells_names[i].lower() + self.edts().insert_device_property(gpio_client_device_id, + property_path_templ.format(gpio_cell_name), cell) + cells_list.append(cell) + + ## + # @brief Extract GPIO ranges + # + # @param node_address Address of node issuing the directive. + # @param prop property name + # @param prop_list list of property values + # + def _extract_gpios(self, node_address, prop, prop_list): + + gpio_client_device_id = self.edts_device_id(node_address) + + gpio_nr_cells = 0 + gpios_index = 0 + gpios_cell_index = 0 + for p in prop_list: + if gpios_cell_index == 0: + gpio_node_address = self.dts().phandles[p] + gpio = self.dts().reduced[gpio_node_address] + gpio_nr_cells = int(gpio['props'].get('#gpio-cells', 2)) + gpios_cells = [] + else: + gpios_cells.append(p) + gpios_cell_index += 1 + if gpios_cell_index > gpio_nr_cells: + # generate EDTS + self.edts_insert_device_controller(gpio_node_address, node_address, + gpios_cells[0]) + self.edts().insert_device_property(gpio_client_device_id, + '{}/{}/controller'.format(prop, gpios_index), + self.edts_device_id(gpio_node_address)) + self.edts_insert_gpio_cells(node_address, gpio_node_address, + gpios_cells, + "{}/{}/{{}}".format(prop, gpios_index)) + gpios_index += 1 + gpios_cell_index = 0 + + ## + # @brief Extract GPIO ranges + # + # @param node_address Address of node issuing the directive. + # @param prop_list list of property values + # + def _extract_gpio_ranges(self, node_address, prop_list): + + gpio_range_cells = 3 + + gpio_device_id = self.edts_device_id(node_address) + gpio_range_index = 0 + gpio_range_cell = 0 + gpio_pin_start = 0 + gpio_pin_count = 0 + pin_controller_node_address = '' + pin_controller_pin_start = 0 + for p in prop_list: + if gpio_range_cell == 0: + pin_controller_node_address = self.dts().phandles[p] + elif gpio_range_cell == 1: + gpio_pin_start = p + elif gpio_range_cell == 2: + pin_controller_pin_start = p + elif gpio_range_cell == 3: + gpio_pin_count = p + if gpio_range_cell < gpio_range_cells: + gpio_range_cell += 1 + else: + # generate EDTS + self.edts().insert_device_property(gpio_device_id, + 'gpio-ranges/{}/pin-controller'.format(gpio_range_index), + self.edts_device_id(pin_controller_node_address)) + self.edts().insert_device_property(gpio_device_id, + 'gpio-ranges/{}/pin-controller-base'.format(gpio_range_index), + int(pin_controller_pin_start)) + self.edts().insert_device_property(gpio_device_id, + 'gpio-ranges/{}/base'.format(gpio_range_index), + int(gpio_pin_start)) + self.edts().insert_device_property(gpio_device_id, + 'gpio-ranges/{}/npins'.format(gpio_range_index), + int(gpio_pin_count)) + + gio_range_cell = 0 + gpio_range_index += 1 + + ## + # @brief Extract gpio related directives + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + properties = self.dts().reduced[node_address]['props'][prop] + + prop_list = [] + if not isinstance(properties, list): + prop_list.append(properties) + else: + prop_list = list(properties) + # Newer versions of dtc might have the property look like + # <1 2>, <3 4>; + # So we need to flatten the list in that case + if isinstance(prop_list[0], list): + prop_list = [item for sublist in prop_list for item in sublist] + + if prop == 'gpio-ranges': + # indicator for gpio + self._extract_gpio_ranges(node_address, prop_list) + elif 'gpios' in prop: + # indicator for gpio client + self._extract_gpios(node_address, prop, prop_list) + else: + raise Exception( + "DTGpio.extract called with unexpected directive ({})." + .format(prop)) + diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/heuristics.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/heuristics.py new file mode 100644 index 000000000000..58f85d93656e --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/heuristics.py @@ -0,0 +1,123 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Generate device tree information based on heuristics. +# +# Generates in EDTS: +# - bus/master : device id of bus master for a bus device +# - parent-device : device id of parent device +class DTHeuristics(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + + ## + # @brief Insert device parent-child relation into EDTS + # + def _edts_insert_device_parent_child_relation(self, node_address): + # Check for a parent device this device is subordinated + parent_device_id = None + parent_node_address = '' + for comp in node_address.split('/')[1:-1]: + parent_node_address += '/' + comp + compatibles = self.dts().get_compats(parent_node_address) + if compatibles: + # there is a parent device, + if 'simple-bus' in compatibles: + # only use the ones that have a minimum control on the child + continue + if ('pin-controller' in parent_node_address) and \ + (self.dts().reduced[node_address]['props'] \ + .get('compatible', None) is None): + # sub nodes of pin controllers should have their own compatible + # otherwise it is a pin configuration node (not a device) + continue + parent_device_id = self.edts_device_id(parent_node_address) + if parent_device_id: + device_id = self.edts_device_id(node_address) + self.edts().insert_device_property(device_id, 'parent-device', + parent_device_id) + if not parent_device_id in self.edts()['devices']: + # parent not in edts - insert now + self.edts().insert_device_property(parent_device_id, + 'child-devices/0', + device_id) + return + child_devices = self.edts()['devices'][parent_device_id].get('child-devices', None) + if child_devices is None: + child_devices = {} + self.edts()['devices'][parent_device_id]['child-devices'] = child_devices + if not device_id in child_devices.values(): + child_devices[str(len(child_devices))] = device_id + + ## + # @brief Generate device tree information based on heuristics. + # + # Device tree properties may have to be deduced by heuristics + # as the property definitions are not always consistent across + # different node types. + # + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + compat = '' + + # Check aliases + if node_address in self.dts().aliases: + for i, alias in enumerate(self.dts().aliases[node_address]): + # extract alias node info + self.edts_insert_device_property(node_address, + 'alias/{}'.format(i), alias) + + # Process compatible related work + compatibles = self.dts().reduced[node_address]['props'].get('compatible', []) + if not isinstance(compatibles, list): + compatibles = [compatibles,] + + # Check for -device that is connected to a bus + for compat in compatibles: + # get device type list + try: + device_types = self.bindings()[compat]['type'] + + if not isinstance(device_types, list): + device_types = [device_types, ] + if not isinstance(device_types, list): + device_types = [device_types, ] + + # inject device type in database + for j, device_type in enumerate(device_types): + self.edts().insert_device_type(compat, device_type) + self.edts_insert_device_property(node_address, + 'device-type/{}'.format(j), + device_type) + except KeyError: + pass + + # Proceed with bus processing + if 'parent' not in self.bindings()[compat]: + continue + + bus_master_device_type = self.bindings()[compat]['parent']['bus'] + + # get parent bindings + parent_node_address = self.dts().get_parent_address(node_address) + parent_binding = self.bindings()[self.dts().reduced[parent_node_address] \ + ['props']['compatible']] + + if bus_master_device_type != parent_binding['child']['bus']: + continue + + # generate EDTS + self.edts_insert_device_property(node_address, 'bus/master', + parent_node_address) + + # Check for a parent device this device is subordinated + self._edts_insert_device_parent_child_relation(node_address) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/interrupts.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/interrupts.py new file mode 100644 index 000000000000..a8200a063270 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/interrupts.py @@ -0,0 +1,88 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage interrupts directives. +# +class DTInterrupts(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + # Ignore properties that are covered by the extraction of: + extractors.ignore_properties.extend([ \ + # - 'interrupts' for interrupt consumers + 'interrupt-names',]) + + def _find_parent_irq_node(self, node_address): + dts = self.dts() + address = '' + + for comp in node_address.split('/')[1:]: + address += '/' + comp + if 'interrupt-parent' in dts.reduced[address]['props']: + interrupt_parent = dts.reduced[address]['props'].get( + 'interrupt-parent') + + return dts.phandles[interrupt_parent] + + ## + # @brief Extract interrupts + # + # @param node_address Address of node owning the + # interrupts definition. + # @param prop compatible property name + # + def extract(self, node_address, prop): + dts = self.dts() + bindings = self.bindings() + edts = self.edts() + + node = dts.reduced[node_address] + + interrupts = node['props']['interrupts'] + if not isinstance(interrupts, list): + interrupts = [interrupts, ] + # Flatten the list in case + if isinstance(interrupts[0], list): + interrupts = [item for sublist in interrupts for item in sublist] + + irq_parent = self._find_parent_irq_node(node_address) + irq_nr_cells = dts.reduced[irq_parent]['props']['#interrupt-cells'] + irq_cell_binding = bindings[dts.get_compat(irq_parent)] + irq_cell_names = irq_cell_binding.get('#interrupt-cells', + irq_cell_binding.get('#cells', [])) + irq_names = node['props'].get('interrupt-names', []) + if not isinstance(irq_names, list): + irq_names = [irq_names, ] + + # generate EDTS + device_id = self.edts_device_id(node_address) + irq_index = 0 + irq_cell_index = 0 + for cell in interrupts: + if len(irq_names) > irq_index: + irq_name = irq_names[irq_index] + else: + irq_name = str(irq_index) + if len(irq_cell_names) > irq_cell_index: + irq_cell_name = irq_cell_names[irq_cell_index] + else: + irq_cell_name = str(irq_cell_index) + edts.insert_device_property(device_id, + 'interrupts/{}/parent'.format(irq_name), + self.edts_device_id(irq_parent)) + edts.insert_device_property(device_id, + 'interrupts/{}/{}'.format(irq_name, irq_cell_name), + cell) + if 'irq' in irq_cell_name: + self.edts_insert_device_controller(irq_parent, node_address, cell) + + irq_cell_index += 1 + if irq_cell_index >= irq_nr_cells: + irq_cell_index = 0 + irq_index += 1 diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/pinctrl.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/pinctrl.py new file mode 100644 index 000000000000..485b970b9e35 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/pinctrl.py @@ -0,0 +1,131 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage pinctrl related directives. +# +# Handles: +# - pinctrl-x +# - pinctrl-names +# +# Generates in EDTS: +# - pinctrl//name : name of the pinctrl +# - pinctrl//pinconf//pin-controller : device_id of +# pin controller +# - pinctrl//pinconf//bias-disable : pinconf value +# - pinctrl//pinconf//bias-high-impedance : .. +# - pinctrl//pinconf//bias-bus-hold : .. +# +class DTPinCtrl(DTDirective): + + ## + # @brief Boolean type properties for pin configuration by pinctrl. + pinconf_bool_props = [ + "bias-disable", "bias-high-impedance", "bias-bus-hold", + "drive-push-pull", "drive-open-drain", "drive-open-source", + "input-enable", "input-disable", "input-schmitt-enable", + "input-schmitt-disable", "low-power-enable", "low-power-disable", + "output-disable", "output-enable", "output-low","output-high"] + ## + # @brief Boolean or value type properties for pin configuration by pinctrl. + pinconf_bool_or_value_props = [ + "bias-pull-up", "bias-pull-down", "bias-pull-pin-default"] + ## + # @brief List type properties for pin configuration by pinctrl. + pinconf_list_props = [ + "pinmux", "pins", "group", "drive-strength", "input-debounce", + "power-source", "slew-rate", "speed"] + + def __init__(self, extractors): + super().__init__(extractors) + # Ignore properties that are covered by the extraction of: + extractors.ignore_properties.extend([ \ + # - 'pinctrl-x' for pin controller clients + 'pinctrl-names',]) + + ## + # @brief Extract pinctrl information. + # + # @param node_address Address of node owning the pinctrl definition. + # @param yaml YAML definition for the owning node. + # @param prop pinctrl-x key + # @param def_label Define label string of client node owning the pinctrl + # definition. + # + def extract(self, node_address, prop): + + node = self.dts().reduced[node_address] + + # Get pinctrl index from pinctrl- directive + pinctrl_index = int(prop.split('-')[1]) + # Pinctrl definition + pinctrl = node['props'][prop] + # Name of this pinctrl state. Use directive if there is no name. + pinctrl_names = node['props'].get('pinctrl-names', []) + if not isinstance(pinctrl_names, list): + pinctrl_names = [pinctrl_names, ] + if pinctrl_index >= len(pinctrl_names): + pinctrl_name = prop + else: + pinctrl_name = pinctrl_names[pinctrl_index] + + pin_config_handles = [] + if not isinstance(pinctrl, list): + pin_config_handles.append(pinctrl) + else: + pin_config_handles = list(pinctrl) + + # generate EDTS pinctrl + pinctrl_client_device_id = self.edts_device_id(node_address) + self.edts().insert_device_property(pinctrl_client_device_id, + 'pinctrl/{}/name'.format(pinctrl_index), pinctrl_name) + + pinconf_index = 0 + for pin_config_handle in pin_config_handles: + pin_config_node_address = self.dts().phandles[pin_config_handle] + pin_controller_node_address = \ + '/'.join(pin_config_node_address.split('/')[:-1]) + pin_config_subnode_prefix = \ + '/'.join(pin_config_node_address.split('/')[-1:]) + pin_controller_device_id = self.edts_device_id(pin_controller_node_address) + for subnode_address in self.dts().reduced: + if pin_config_subnode_prefix in subnode_address \ + and pin_config_node_address != subnode_address: + # Found a subnode underneath the pin configuration node + # Create pinconf defines and EDTS + self.edts().insert_device_property(pinctrl_client_device_id, + 'pinctrl/{}/pinconf/{}/name' \ + .format(pinctrl_index, pinconf_index), + subnode_address.split('/')[-1]) + self.edts().insert_device_property(pinctrl_client_device_id, + 'pinctrl/{}/pinconf/{}/pin-controller' \ + .format(pinctrl_index, pinconf_index), + pin_controller_device_id) + pinconf_props = self.dts().reduced[subnode_address]['props'].keys() + for pinconf_prop in pinconf_props: + pinconf_value = self.dts().reduced[subnode_address]['props'][pinconf_prop] + if pinconf_prop in self.pinconf_bool_props: + pinconf_value = 1 if pinconf_value else 0 + elif pinconf_prop in self.pinconf_bool_or_value_props: + if isinstance(pinconf_value, bool): + pinconf_value = 1 if pinconf_value else 0 + elif pinconf_prop in self.pinconf_list_props: + if not isinstance(pinconf_value, list): + pinconf_value = [pinconf_value, ] + else: + # should become an exception + print("pinctrl.py: Pin control pin configuration", + "'{}'".format(pinconf_prop), + "not supported - ignored.") + # generate EDTS pinconf value + self.edts().insert_device_property(pinctrl_client_device_id, + 'pinctrl/{}/pinconf/{}/{}'.format(pinctrl_index, + pinconf_index, + pinconf_prop), + pinconf_value) + pinconf_index += 1 diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/pwm.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/pwm.py new file mode 100644 index 000000000000..c32c369e89d8 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/pwm.py @@ -0,0 +1,145 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage pwm directives. +# +# Handles: +# - pwms +# - pwm-names +# +# Generates in EDTS: +# - -controller : True +class DTPWM(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + # Ignore properties that are covered by the extraction of + extractors.ignore_properties.extend([ \ + # - 'pwms' for pwm clients + 'pwm-names', ]) + + ## + # @brief Insert pwm cells into EDTS + # + # @param pwm_client + # @param bindings + # @param pwm_controller + # @param pwm_cells + # @param property_path_templ "xxx/yyy/{}" + # + def edts_insert_pwm_cells(self, pwm_client_node_address, + pwm_controller_node_address, pwm_cells, + property_path_templ): + dts = self.dts() + bindings = self.bindings() + edts = self.edts() + + if len(pwm_cells) == 0: + return + pwm_client_device_id = self.edts_device_id(pwm_client_node_address) + pwm_controller_device_id = self.edts_device_id(pwm_controller_node_address) + pwm_controller = dts.reduced[pwm_controller_node_address] + pwm_controller_compat = dts.get_compat(pwm_controller_node_address) + pwm_controller_bindings = bindings[pwm_controller_compat] + pwm_nr_cells = int(pwm_controller['props'].get('#pwm-cells', 0)) + pwm_cells_names = pwm_controller_bindings.get( + '#pwm-cells', ['ID', 'CELL1', "CELL2", "CELL3"]) + for i, cell in enumerate(pwm_cells): + if i >= len(pwm_cells_names): + pwm_cell_name = 'CELL{}'.format(i).lower() + else: + pwm_cell_name = pwm_cells_names[i].lower() + edts.insert_device_property(pwm_client_device_id, + property_path_templ.format(pwm_cell_name), cell) + + def _extract_client(self, node_address, prop): + dts = self.dts() + edts = self.edts() + + pwms = dts.reduced[node_address]['props']['pwms'] + + if not isinstance(pwms, list): + pwms = [pwms, ] + # Flatten the list in case + if isinstance(pwms[0], list): + pwms = [item for sublist in pwms for item in sublist] + + pwm_client_device_id = self.edts_device_id(node_address) + pwm_client = dts.reduced[node_address] + pwm_client_pwm_names = pwm_client['props'].get('pwm-names', None) + + pwm_index = 0 + pwm_cell_index = 0 + pwm_nr_cells = 0 + pwm_controller_node_address = '' + pwm_controller = {} + for cell in pwms: + if pwm_cell_index == 0: + if cell not in dts.phandles: + raise Exception( + ("Could not find the pwm controller node {} for pwms" + " = {} in pwm client node {}. Did you activate" + " the pwm node?. Last pwm controller: {}.") + .format(str(cell), str(pwms), node_address, + str(pwm_controller))) + pwm_controller_node_address = dts.phandles[cell] + pwm_controller = dts.reduced[pwm_controller_node_address] + pwm_nr_cells = int(pwm_controller['props'].get('#pwm-cells', 0)) + pwm_cells = [] + else: + pwm_cells.append(cell) + pwm_cell_index += 1 + if pwm_cell_index > pwm_nr_cells: + if len(pwm_client_pwm_names) > pwm_index: + pwm_name = pwm_client_pwm_names[pwm_index] + else: + pwm_name = str(pwm_index) + # generate EDTS + edts.insert_device_property(pwm_client_device_id, + 'pwms/{}/controller'.format(pwm_name), + self.edts_device_id(pwm_controller_node_address)) + self.edts_insert_pwm_cells(node_address, + pwm_controller_node_address, pwm_cells, + "pwms/{}/{{}}".format(pwm_name)) + + pwm_cell_index = 0 + pwm_index += 1 + + def _extract_controller(self, node_address, prop): + dts = self.dts() + + pwm_controller = dts.reduced[node_address] + # generate EDTS + pwm_controller_device_id = self.edts_device_id(node_address) + # TODO + + ## + # @brief Extract pwm related directives + # + # @param edts Extended device tree database + # @param dts Device tree specification + # @param bindings Device tree bindings + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + if prop == 'pwms': + # indicator for pwm clients + self._extract_client(node_address, prop) + elif prop in ('pwm-names', ): + # covered by _extract_client + pass + elif prop == '#pwm-cells': + # indicator for pwm controllers + self._extract_controller(node_address, prop) + else: + raise Exception( + "DTPWM.extract called with unexpected directive ({})." + .format(prop)) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/extractors/reg.py b/scripts/cogeno/cogeno/modules/edtsdb/extractors/reg.py new file mode 100644 index 000000000000..02247123e139 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/extractors/reg.py @@ -0,0 +1,74 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +from .directive import DTDirective + +## +# @brief Manage reg directive. +# +class DTReg(DTDirective): + + def __init__(self, extractors): + super().__init__(extractors) + # Ignore properties that are covered by the extraction of + extractors.ignore_properties.extend([ \ + # - 'reg' + 'reg-names', ]) + + ## + # @brief Extract reg directive info + # + # @param edts Extended device tree database + # @param dts Device tree specification + # @param bindings Device tree bindings + # @param node_address Address of node issuing the directive. + # @param prop Directive property name + # + def extract(self, node_address, prop): + + node = self.dts().reduced[node_address] + node_compat = self.dts().get_compat(node_address) + + reg = self.dts().reduced[node_address]['props']['reg'] + if not isinstance(reg, list): + reg = [reg, ] + # Newer versions of dtc might have the reg property look like + # reg = <1 2>, <3 4>; + # So we need to flatten the list in that case + if isinstance(reg[0], list): + reg = [item for sublist in reg for item in sublist] + + (nr_address_cells, nr_size_cells) = self.dts().get_addr_size_cells(node_address) + + # generate EDTS + device_id = self.edts_device_id(node_address) + reg_index = 0 + reg_cell_index = 0 + reg_nr_cells = nr_address_cells + nr_size_cells + reg_names = node['props'].get('reg-names', []) + # if there is a single reg-name, we get a string, not a list back + # turn it into a list so things are normalized + if not isinstance(reg_names, list): + reg_names = [reg_names, ] + for cell in reg: + if len(reg_names) > reg_index: + reg_name = reg_names[reg_index] + else: + reg_name = str(reg_index) + if reg_cell_index < nr_address_cells: + cell += self.dts().translate_addr(cell, node_address, + nr_address_cells, + nr_size_cells) + self.edts().insert_device_property(device_id, + 'reg/{}/address/{}'.format(reg_name, reg_cell_index), cell) + else: + size_cell_index = reg_cell_index - nr_address_cells + self.edts().insert_device_property(device_id, + 'reg/{}/size/{}'.format(reg_name, size_cell_index), cell) + reg_cell_index += 1 + if reg_cell_index >= reg_nr_cells: + reg_cell_index = 0 + reg_index += 1 diff --git a/scripts/cogeno/cogeno/modules/edtsdb/headermaker.py b/scripts/cogeno/cogeno/modules/edtsdb/headermaker.py new file mode 100644 index 000000000000..ea90b40e1977 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/headermaker.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018 Bobby Noelte +# Copyright (c) 2018 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +import json +from pathlib import Path + +## +# @brief ETDS Database provider +# +# Methods for ETDS database creation. +# +class EDTSHeaderMakerMixin(object): + __slots__ = [] + + @staticmethod + def _convert_string_to_label(s): + # Transmute ,-@/ to _ + s = s.replace("-", "_") + s = s.replace(",", "_") + s = s.replace("@", "_") + s = s.replace("/", "_") + # Uppercase the string + s = s.upper() + return s + + ## + # @brief Write header file + # + # @param file_path Path of the file to write + # + def export_header(self, file_path): + header = "/* device tree header file - do not change - generated automatically */\n" + for device_id in self._edts['devices']: + path_prefix = device_id + ':' + device = cogeno.edts().get_device_by_device_id(device_id) + properties = device.get_properties_flattened(path_prefix) + label = device.get_property('label').strip('"') + for prop_path, prop_value in properties.items(): + header_label = self._convert_string_to_label( + "DT_{}_{}".format(label, prop_path)) + header += "#define {}\t{}\n".format(header_label, prop_value) + with Path(file_path).open(mode="w", encoding="utf-8") as save_file: + save_file.write(header) diff --git a/scripts/cogeno/cogeno/modules/edtsdb/provider.py b/scripts/cogeno/cogeno/modules/edtsdb/provider.py new file mode 100644 index 000000000000..f77a15862b1e --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/provider.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018 Bobby Noelte +# Copyright (c) 2018 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +import json +from pathlib import Path + +## +# @brief ETDS Database provider +# +# Methods for ETDS database creation. +# +class EDTSProviderMixin(object): + __slots__ = [] + + def _update_device_compatible(self, device_id, compatible): + if compatible not in self._edts['compatibles']: + self._edts['compatibles'][compatible] = list() + if device_id not in self._edts['compatibles'][compatible]: + self._edts['compatibles'][compatible].append(device_id) + self._edts['compatibles'][compatible].sort() + + def insert_chosen(self, chosen, device_id): + self._edts['chosen'][chosen] = device_id + + def _update_device_alias(self, device_id, alias): + if alias not in self._edts['aliases']: + self._edts['aliases'][alias] = list() + if device_id not in self._edts['aliases'][alias]: + self._edts['aliases'][alias].append(device_id) + self._edts['aliases'][alias].sort() + + + def _inject_cell(self, keys, property_access_point, property_value): + + for i in range(0, len(keys)): + if i < len(keys) - 1: + # there are remaining keys + if keys[i] not in property_access_point: + property_access_point[keys[i]] = dict() + property_access_point = property_access_point[keys[i]] + else: + # we have to set the property value + if keys[i] in property_access_point and \ + property_access_point[keys[i]] != property_value: + # Property is already set and we're overwiting with a new + # different value. Trigger an error + raise Exception("Overwriting edts cell {} with different value\n \ + Initial value: {} \n \ + New value: {}".format(property_access_point, + property_access_point[keys[i]], + property_value + )) + property_access_point[keys[i]] = property_value + + + def insert_device_type(self, compatible, device_type): + if device_type not in self._edts['device-types']: + self._edts['device-types'][device_type] = list() + if compatible not in self._edts['device-types'][device_type]: + self._edts['device-types'][device_type].append(compatible) + self._edts['device-types'][device_type].sort() + + def insert_device_controller(self, controller_type, controller, device_id, line): + property_access_point = self._edts['controllers'] + keys = [controller_type, controller] + if not isinstance(line, list): + line = [ line, ] + keys.extend(line) + for i in range(0, len(keys)): + if i < len(keys) - 1: + # there are remaining keys + if keys[i] not in property_access_point: + property_access_point[keys[i]] = dict() + property_access_point = property_access_point[keys[i]] + elif i == len(keys) - 1: + if keys[i] not in property_access_point: + property_access_point[keys[i]] = list() + property_access_point[keys[i]].append(device_id) + + ## + # @brief Insert property value for the child of a device id. + # + # @param device_id + # @param child_name + # @param property_path Path of the property to access + # (e.g. 'reg/0', 'interrupts/prio', 'label', ...) + # @param property_value value + # + def insert_child_property(self, device_id, child_name, property_path, property_value): + + # normal property management + if device_id not in self._edts['devices']: + self._edts['devices'][device_id] = dict() + self._edts['devices'][device_id]['device-id'] = device_id + + # Inject prop + keys = ['children', child_name] + prop_keys = property_path.strip("'").split('/') + keys.extend(prop_keys) + property_access_point = self._edts['devices'][device_id] + self._inject_cell(keys, property_access_point, property_value) + + # Compute and inject unique device-id + keys = ['device-id'] + child_id = device_id + '/' + child_name + property_access_point = self._edts['devices'][device_id]['children']\ + [child_name] + self._inject_cell(keys, property_access_point, child_id) + + # Update alias struct if needed + if property_path.startswith('alias'): + self._update_device_alias(child_id, property_value) + + ## + # @brief Insert property value for the device of the given device id. + # + # @param device_id + # @param property_path Path of the property to access + # (e.g. 'reg/0', 'interrupts/prio', 'label', ...) + # @param property_value value + # + def insert_device_property(self, device_id, property_path, property_value): + + # special properties + if property_path.startswith('compatible'): + self._update_device_compatible(device_id, property_value) + if property_path.startswith('alias'): + aliased_device = device_id + if 'children' in property_path: + # device_id is the parent_id + # property_path is children/device_id/alias + aliased_device = property_path.split('/')[1] + self._update_device_alias(aliased_device, property_value) + + # normal property management + if device_id not in self._edts['devices']: + self._edts['devices'][device_id] = dict() + self._edts['devices'][device_id]['device-id'] = device_id + if property_path == 'device-id': + # already set + return + keys = property_path.strip("'").split('/') + property_access_point = self._edts['devices'][device_id] + + self._inject_cell(keys, property_access_point, property_value) + + ## + # @brief Write json file + # + # @param file_path Path of the file to write + # + def save(self, file_path): + with Path(file_path).open(mode="w", encoding="utf-8") as save_file: + json.dump(self._edts, save_file, indent = 4, sort_keys=True) From 6d7e50f3ca9f787e70a178f2f9c33f33504f97f8 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 2 Nov 2018 16:19:31 +0100 Subject: [PATCH 03/13] scripts: cogeno: edts: add generic bindings for EDTS extraction These are generic bindings that the extraction function of the EDTS database can work with. The bindings can be overriden by providing a same named file to cogeno within a directory that is given in the --bindings option. Signed-off-by: Bobby Noelte --- .../cogeno/modules/edtsdb/bindings/adc.yaml | 31 ++++++++ .../modules/edtsdb/bindings/can-device.yaml | 32 +++++++++ .../cogeno/modules/edtsdb/bindings/can.yaml | 60 ++++++++++++++++ .../edtsdb/bindings/clock-consumer.yaml | 72 +++++++++++++++++++ .../edtsdb/bindings/clock-provider.yaml | 51 +++++++++++++ .../modules/edtsdb/bindings/dma-client.yaml | 41 +++++++++++ .../cogeno/modules/edtsdb/bindings/dma.yaml | 46 ++++++++++++ .../modules/edtsdb/bindings/ethernet.yaml | 23 ++++++ .../modules/edtsdb/bindings/fixed-clock.yaml | 52 ++++++++++++++ .../edtsdb/bindings/fixed-partitions.yaml | 20 ++++++ .../edtsdb/bindings/flash-controller.yaml | 39 ++++++++++ .../cogeno/modules/edtsdb/bindings/flash.yaml | 27 +++++++ .../modules/edtsdb/bindings/gpio-keys.yaml | 32 +++++++++ .../modules/edtsdb/bindings/gpio-leds.yaml | 32 +++++++++ .../modules/edtsdb/bindings/gpio-pinctrl.yaml | 53 ++++++++++++++ .../modules/edtsdb/bindings/i2c-device.yaml | 33 +++++++++ .../cogeno/modules/edtsdb/bindings/i2c.yaml | 47 ++++++++++++ .../cogeno/modules/edtsdb/bindings/i2s.yaml | 34 +++++++++ .../modules/edtsdb/bindings/partitions.yaml | 50 +++++++++++++ .../modules/edtsdb/bindings/pinctrl.yaml | 39 ++++++++++ .../modules/edtsdb/bindings/pwm-client.yaml | 32 +++++++++ .../cogeno/modules/edtsdb/bindings/pwm.yaml | 43 +++++++++++ .../cogeno/modules/edtsdb/bindings/rtc.yaml | 45 ++++++++++++ .../modules/edtsdb/bindings/soc-nv-flash.yaml | 20 ++++++ .../modules/edtsdb/bindings/spi-device.yaml | 38 ++++++++++ .../cogeno/modules/edtsdb/bindings/spi.yaml | 50 +++++++++++++ .../modules/edtsdb/bindings/uart-device.yaml | 27 +++++++ .../cogeno/modules/edtsdb/bindings/uart.yaml | 47 ++++++++++++ .../modules/edtsdb/bindings/usb-ep.yaml | 37 ++++++++++ .../cogeno/modules/edtsdb/bindings/usb.yaml | 35 +++++++++ 30 files changed, 1188 insertions(+) create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/adc.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/can-device.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/can.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-consumer.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-provider.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/dma-client.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/dma.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/ethernet.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-clock.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-partitions.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/flash-controller.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/flash.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-keys.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-leds.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-pinctrl.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c-device.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/i2s.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/partitions.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/pinctrl.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm-client.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/rtc.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/soc-nv-flash.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/spi-device.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/spi.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/uart-device.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/uart.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/usb-ep.yaml create mode 100644 scripts/cogeno/cogeno/modules/edtsdb/bindings/usb.yaml diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/adc.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/adc.yaml new file mode 100644 index 000000000000..61ab73a23d19 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/adc.yaml @@ -0,0 +1,31 @@ +# +# Copyright (c) 2017, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: ADC Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all ADC devices + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + clocks: + type: array + category: required + description: Clock gate information + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/can-device.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/can-device.yaml new file mode 100644 index 000000000000..3b671d563a0b --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/can-device.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018 Alexander Wachter +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: CAN Device Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all can devices + +parent: + bus: can + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + reg: + type: array + description: register base address + generation: define + category: required + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/can.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/can.yaml new file mode 100644 index 000000000000..d5463ed31d69 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/can.yaml @@ -0,0 +1,60 @@ +--- +title: CAN Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all CAN devices + +child: + bus: can + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + "#address-cells": + type: int + category: required + description: should be 1. + "#size-cells": + type: int + category: required + description: should be 0. + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + clocks: + type: array + category: required + description: Clock gate information + generation: define + bus-speed: + type: int + category: required + description: bus speed in Baud/s + generation: define + sjw: + type: int + category: required + description: Resynchronization jump width (ISO 11898-1) + generation: define + prop_seg_phase_seg1: + type: int + category: required + description: Time quantums of phase buffer 1 segment + propagation segment (ISO 11898-1) + generation: define + phase_seg2: + type: int + category: required + description: Time quantums of phase buffer 2 segment (ISO 11898-1) + generation: define + pinctrl-\d+: + type: array + category: optional + description: pinmux information for RX, TX + generation: structure +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-consumer.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-consumer.yaml new file mode 100644 index 000000000000..03511485bf49 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-consumer.yaml @@ -0,0 +1,72 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# + +# -- Assigned clock parents and rates -- +# Some platforms may require initial configuration of default parent clocks +# and clock frequencies. Such a configuration can be specified in a device tree +# node through assigned-clocks, assigned-clock-parents and assigned-clock-rates +# properties. +--- +title: Clock Consumer Base Structure +type: clock-consumer +version: 0.1 + +description: > + This binding gives the base structures for all clock consumers. + +properties: + + clocks: + type: array + category: required + description: > + List of phandle and clock specifier pairs, one pair for each clock + input to the device. Note - if the clock provider specifies '0' for + clock-cells, then only the phandle portion of the pair will appear. + generation: define + + clock-names: + type: array + category: optional + description: > + List of clock input name strings sorted in the same order as the clocks + property. + generation: define + + clock-ranges: + type: empty + category: optional + description: > + Empty property indicating that child nodes can inherit named clocks from + this node. Useful for bus nodes to provide a clock to their children. + generation: define + + assigned-clocks: + type: array + category: optional + description: > + List of phandle and clock specifier pairs, one pair for each assigned + clock input. Note - if the clock provider specifies '0' for + clock-cells, then only the phandle portion of the pair will appear. + generation: define + + assigned-clock-parents: + type: array + category: optional + description: > + List of parent clocks in the form of a phandle and clock + specifier pair. The list shall correspond to the clocks listed in the + assigned-clocks directive. + generation: define + + assigned-clock-rates: + type: array + category: optional + description: > + List of frequencies in Hz. The list shall correspond to the clocks + listed in the assigned-clocks directive. + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-provider.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-provider.yaml new file mode 100644 index 000000000000..b0631bde7e19 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/clock-provider.yaml @@ -0,0 +1,51 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Clock Provider Base Structure +type: clock-provider +version: 0.1 + +description: > + This binding gives the base structures for all clock providers. + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + label: + type: string + category: required + description: Human readable string describing the clock (used by Zephyr for API name) + generation: define + + "#clock-cells": + type: int + category: required + description: > + This device is providing controllable clocks, the + clock_cells property needs to be specified. + generation: define + + clock-output-names: + type: array + category: optional + description: > + A list of strings of clock output signal names indexed by the first + cell in the clock specifier. + generation: define + + clock-indices: + type: array + category: optional + description: > + The identifying number for the clocks in the node. If it is not linear + from zero, then this allows the mapping of identifiers into the + clock-output-names array. + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/dma-client.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/dma-client.yaml new file mode 100644 index 000000000000..fb0630e2cdf3 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/dma-client.yaml @@ -0,0 +1,41 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: DMA Client Base Structure +type: dma-client +version: 0.1 + +description: > + This binding gives the base structures for all DMA clients. + +properties: + + dmas: + type: array + category: required + description: > + List of one or more DMA specifiers, each consisting of + - A phandle pointing to DMA controller node + - A number of integer cells, as determined by the + dma-cells property in the node referenced by phandle + containing DMA controller specific information. This + typically contains a DMA request line number or a + channel number, but can contain any data that is + required for configuring a channel.. + generation: define + + dma-names: + type: array + category: optional + description: > + Contains one identifier string for each DMA specifier in + the dmas property. The specific strings that can be used + are defined in the binding of the DMA client device. + Multiple DMA specifiers can be used to represent + alternatives and in this case the dma-names for those + DMA specifiers must be identical (see examples). + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/dma.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/dma.yaml new file mode 100644 index 000000000000..20fe8c29fe91 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/dma.yaml @@ -0,0 +1,46 @@ +# +# Copyright (c) 2018, Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: DMA Base Structure +type: dma +version: 0.1 + +description: > + This binding gives the base structures for all DMA devices + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + "#dma-cells": + type: int + category: required + description: > + Number of cells used to specify a DMA. + + dma-channels: + type: int + category: required + description: > + Number of DMA channels supported by the controller. + generation: define + + dma-requests: + type: int + category: required + description: > + Number of DMA request signals supported by the controller + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/ethernet.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/ethernet.yaml new file mode 100644 index 000000000000..288e89f70be2 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/ethernet.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2018, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Ethernet Base Structure +version: 0.1 + +description: > + This binding gives a base structures for all Ethernet devices +properties: + local-mac-address: + type: array + category: optional + description: mac address + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-clock.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-clock.yaml new file mode 100644 index 000000000000..bd22f66ac1b9 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-clock.yaml @@ -0,0 +1,52 @@ +# +# Copyright (c) 2017, b0661n0e17e@gmail.com +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Simple fixed-rate clock sources. +type: fixed-clock +version: 0.1 + +description: > + Binding for simple fixed-rate clock sources. + +inherits: + !include clock-provider.yaml + +properties: + compatible: + constraint: "fixed-clock" + + "#clock-cells": + constraint: 0 + + clock-frequency: + type: int + category: required + description: Frequency of clock in Hz. Should be a single cell. + generation: define + + clock-accuracy: + type: int + category: optional + description: Accuracy of clock in ppb (parts per billion). Should be a single cell. + generation: define + + oscillator: + type: int + category: optional + description: clock is an oszillator (a quartz) + generation: define + + pinctrl-\d+: + type: array + category: optional + description: pinmux information for clock IN, OUT + generation: define + + pinctrl-names: + type: array + description: names to assign to states + category: required +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-partitions.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-partitions.yaml new file mode 100644 index 000000000000..6825ab1c8ec8 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/fixed-partitions.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Fixed Partitions Base Structure +type: fixed-partitions +version: 0.1 + +description: > + This binding gives the base structures for all fixed partitions. + +inherits: + !include partitions.yaml + +properties: + compatible: + constraint: "fixed-partitions" +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/flash-controller.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/flash-controller.yaml new file mode 100644 index 000000000000..2c03310a049c --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/flash-controller.yaml @@ -0,0 +1,39 @@ +--- +title: flash controller Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all flash controller devices + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + + reg: + type: array + description: mmio register space + generation: define + category: required + + interrupts: + type: array + category: optional + description: required interrupts + generation: define + + interrupt-names: + type: stringlist + category: optional + description: names of required interrupts + generation: define + +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/flash.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/flash.yaml new file mode 100644 index 000000000000..521fcd8f374c --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/flash.yaml @@ -0,0 +1,27 @@ +--- +title: Flash Base Structure +type: flash +version: 0.1 + +description: > + This binding gives the base structures for all flash devices + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + + reg: + type: array + description: mmio register space + generation: define + category: required +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-keys.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-keys.yaml new file mode 100644 index 000000000000..b3f8e1b909b4 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-keys.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: GPIO KEYS +version: 0.1 + +description: > + This is a representation of the GPIO KEYS nodes + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "gpio-keys" + generation: define + + gpios: + type: compound + category: required + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-leds.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-leds.yaml new file mode 100644 index 000000000000..a00c6f1190df --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-leds.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: GPIO LED +version: 0.1 + +description: > + This is a representation of the LED GPIO nodes + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "gpio-leds" + generation: define + + gpios: + type: compound + category: required + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-pinctrl.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-pinctrl.yaml new file mode 100644 index 000000000000..576d804d46c4 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/gpio-pinctrl.yaml @@ -0,0 +1,53 @@ +--- +title: GPIO with PINCTRL base structure +type: gpio-pinctrl +version: 0.1 + +description: > + This binding gives the base structures for all GPIO devices node using a PINCTRL backend + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + gpio-controller: + type: string + category: required + description: device controller identification + generation: define + + compatible: + type: string + category: required + description: compatible strings + + "#gpio-cells": + type: int + category: required + description: should be 2. + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for device name) + generation: define + + gpio-ranges: + type: array + category: optional + description: gpio range in pin controller + generation: define + + pinctrl-\d+: + type: array + category: optional + description: pinmux information for GPIO I/0 + generation: structure + +"#cells": + - pin + - flags +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c-device.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c-device.yaml new file mode 100644 index 000000000000..10c95e259679 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c-device.yaml @@ -0,0 +1,33 @@ +# +# Copyright (c) 2017, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: I2C Device Base Structure +type: i2c-device +version: 0.1 + +description: > + This binding gives the base structures for all i2c devices + +parent: + bus: i2c + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + reg: + type: array + description: address on i2c bus + generation: define + category: required + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c.yaml new file mode 100644 index 000000000000..e5ff9397f482 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2c.yaml @@ -0,0 +1,47 @@ +# +# Copyright (c) 2017 I-SENSE group of ICCS +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: I2C Base Structure +type: i2c +version: 0.1 + +description: > + This binding gives the base structures for all I2C devices + +child: + bus: i2c + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + "#address-cells": + type: int + category: required + description: should be 1. + "#size-cells": + type: int + category: required + description: should be 0. + clock-frequency : + type: int + category: optional + description: Initial clock frequency in Hz + generation: define + clocks: + type: array + category: required + description: Clock gate information + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2s.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2s.yaml new file mode 100644 index 000000000000..ae172c654853 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/i2s.yaml @@ -0,0 +1,34 @@ +# +# Copyright (c) 2018, STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: I2S Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all I2S devices + +child: + bus: i2s + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + clocks: + type: array + category: optional + description: Clock gate information + generation: define + + +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/partitions.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/partitions.yaml new file mode 100644 index 000000000000..3ea1dd91f281 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/partitions.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Partitions Base Structure +type: partitions +version: 0.1 + +description: > + This binding gives the base structures for all partitions. + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + "#address-cells": + type: int + category: required + description: > + <1>: for partitions that require a single 32-bit cell to represent their + size/address (aka the value is below 4 GiB) + <2>: for partitions that require two 32-bit cells to represent their + size/address (aka the value is 4 GiB or greater). + + "#size-cells": + type: int + category: required + description: > + <1>: for partitions that require a single 32-bit cell to represent their + size/address (aka the value is below 4 GiB) + <2>: for partitions that require two 32-bit cells to represent their + size/address (aka the value is 4 GiB or greater). + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + + reg: + type: array + description: partition sectors address/ size + generation: define + category: required +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/pinctrl.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/pinctrl.yaml new file mode 100644 index 000000000000..5a1bd500f755 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/pinctrl.yaml @@ -0,0 +1,39 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Pinctrl Base Structure +type: pinctrl +version: 0.1 + +description: > + This binding gives the base structures for all pin controller devices + +properties: + pin-controller: + type: string + category: required + description: device controller identification + generation: define + + compatible: + type: string + category: required + description: compatible strings + generation: define + + "#pinctrl-cells": + type: int + category: required + description: > + Number of pin control cells in addition to the index within the + pin controller device instance + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for device name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm-client.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm-client.yaml new file mode 100644 index 000000000000..4a67f050692b --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm-client.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: PWM Client Base Structure +id: pwm-client +version: 0.1 + +description: > + This binding gives the base structures for all pwm clients. + +properties: + + pwms: + type: array + category: required + description: > + List of phandle and pwm specifiers, one set for each pwm + input to the device. Note - The number of pwm specifiers is + given by the pwm-cells property of the pwm provider. + generation: define + + pwm-names: + type: array + category: optional + description: > + List of strings to label each of the PWM devices sorted in the same + order as the pwms property. + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm.yaml new file mode 100644 index 000000000000..8ec8244013f6 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/pwm.yaml @@ -0,0 +1,43 @@ +# +# Copyright (c) 2017, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: PWM Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all PWM devices + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + "#pwm-cells": + type: int + category: required + description: > + Number of cells used to specify a PWM. + + clocks: + type: array + category: required + description: Clock gate information + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + + pinctrl-\d+: + type: array + category: optional + description: pinmux information for PWMx output + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/rtc.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/rtc.yaml new file mode 100644 index 000000000000..1e1b056dcc6a --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/rtc.yaml @@ -0,0 +1,45 @@ +# +# Copyright (c) 2018, blik GmbH +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: RTC Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all RTC devices + + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + clock-frequency: + type: int + category: optional + description: Clock frequency information for RTC operation + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + interrupts: + type: array + category: required + description: required interrupts + generation: define + interrupt-names: + type: stringlist + category: optional + description: names of required interrupts + generation: define + prescaler: + type: int + category: required + description: Prescaler value denotes number of RTC ticks in 1 second + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/soc-nv-flash.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/soc-nv-flash.yaml new file mode 100644 index 000000000000..840a8e2baa78 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/soc-nv-flash.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: SoC Non Volatile Flash Base Structure +type: soc-nv-flash +version: 0.1 + +description: > + This binding gives the base structures for all SoC flashes. + +inherits: + !include flash.yaml + +properties: + compatible: + constraint: "soc-nv-flash" +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/spi-device.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/spi-device.yaml new file mode 100644 index 000000000000..ce82e049c022 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/spi-device.yaml @@ -0,0 +1,38 @@ +# +# Copyright (c) 2018, I-SENSE group of ICCS +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: SPI Device Base Structure +type: spi-device +version: 0.1 + +description: > + This binding gives the base structures for all spi devices + +parent: + bus: spi + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + reg: + type: array + description: Chip select address of device + generation: define + category: required + spi-max-frequency: + type: u32 + category: required + description: Maximum clock frequency of device's SPI interface in Hz + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/spi.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/spi.yaml new file mode 100644 index 000000000000..e0a0eaffc1a6 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/spi.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2018, I-SENSE group of ICCS +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: SPI Base Structure +type: spi +version: 0.1 + +description: > + This binding gives the base structures for all SPI devices + +child: + bus: spi + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + "#address-cells": + type: int + category: required + description: should be 1. + "#size-cells": + type: int + category: required + description: should be 0. + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + clocks: + type: array + category: optional + description: Clock gate information + generation: define + cs-gpios: + type: compound + category: optional + generation: define, use-prop-name + pinctrl-\d+: + type: array + category: optional + description: pinmux information for SCK, MOSI, MISO + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/uart-device.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/uart-device.yaml new file mode 100644 index 000000000000..0b9342d2bfc0 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/uart-device.yaml @@ -0,0 +1,27 @@ +# +# Copyright (c) 2018, Foundries.io +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: UART Device Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all uart devices + +parent: + bus: uart + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/uart.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/uart.yaml new file mode 100644 index 000000000000..bd7201ddb737 --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/uart.yaml @@ -0,0 +1,47 @@ +--- +title: Uart Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all UART devices + +child: + bus: uart + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + clock-frequency: + type: int + category: optional + description: Clock frequency information for UART operation + generation: define + current-speed: + type: int + category: required + description: Initial baud rate setting for UART + generation: define + clocks: + type: array + category: optional + description: Clock gate information + generation: define + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + interrupt-names: + type: stringlist + category: optional + description: names of required interrupts + generation: define + pinctrl-\d+: + type: array + category: optional + description: pinmux information for RX, TX, CTS, RTS + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/usb-ep.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/usb-ep.yaml new file mode 100644 index 000000000000..eec97d1a4aba --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/usb-ep.yaml @@ -0,0 +1,37 @@ +# +# Copyright (c) 2018, I-SENSE group of ICCS +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: USB Endpoints' properties +version: 0.1 + +description: > + This binding gives number of endpoints that the USB hardware supports + +inherits: + !include usb.yaml + +properties: + num-bidir-endpoints: + type: int + category: required + description: Number of bi-directional endpoints supported by hardware + (including EP0) + generation: define + + num-in-endpoints: + type: int + category: optional + description: Number of IN endpoints supported by hardware + (including EP0 IN) + generation: define + + num-out-endpoints: + type: int + category: optional + description: Number of OUT endpoints supported by hardware + (including EP0 OUT) + generation: define +... diff --git a/scripts/cogeno/cogeno/modules/edtsdb/bindings/usb.yaml b/scripts/cogeno/cogeno/modules/edtsdb/bindings/usb.yaml new file mode 100644 index 000000000000..77fb17be488f --- /dev/null +++ b/scripts/cogeno/cogeno/modules/edtsdb/bindings/usb.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2018, I-SENSE group of ICCS +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: USB Base Structure +version: 0.1 + +description: > + This binding gives the base structures for all USB devices + +properties: + compatible: + type: string + category: required + description: compatible strings + generation: define + + maximum-speed: + type: string + category: optional + description: Configures USB controllers to work up to a specific + speed. Valid arguments are "super-speed", "high-speed", + "full-speed" and "low-speed". If this is not passed + via DT, USB controllers should use their maximum + hardware capability. + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define +... From b6d36f4877de266bac34769d9552e589c02007cd Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 1 Jun 2018 07:56:23 +0200 Subject: [PATCH 04/13] templates: add Zephyr device declaration for code generation Modules and templates for Zephyr device declaration using code generation by cogeno. devicedeclare.py: a python module to support codegen python templates and cogeno inline templates devicedeclare.jinja2: a jinja2 template to support cogeno jinja2 templates and codegen inline templates Signed-off-by: Bobby Noelte --- templates/devicedeclare.jinja2 | 99 +++++++++++ templates/devicedeclare.py | 316 +++++++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 templates/devicedeclare.jinja2 create mode 100644 templates/devicedeclare.py diff --git a/templates/devicedeclare.jinja2 b/templates/devicedeclare.jinja2 new file mode 100644 index 000000000000..92a8e09814dc --- /dev/null +++ b/templates/devicedeclare.jinja2 @@ -0,0 +1,99 @@ +{# Copyright (c) 2018 Nordic Semiconductor ASA #} +{# Copyright (c) 2018 Bobby Noelte #} +{# SPDX-License-Identifier: Apache-2.0 #} + +{%- macro get_param(params, param) -%} + {%- for key, value in params.items() -%} + {%- if key == param -%} + {{ value }} + {%- break -%} + {%- else -%} + {{'None' if loop.last }} + {%- endif -%} + {%- endfor -%} +{%- endmacro %} + +{%- macro gen_device_name(device) -%} + {%- set name = device.get_property('compatible/0')|replace(',','_')|replace('-','_') -%} + {{ name + "_%x"|format(device.select_property('reg', 'FIRST', 'address', 'FIRST')) }} +{%- endmacro -%} + +{%- macro gen_irq_connect(device, params) -%} +{% for irq_name in device.get_property('interrupts') %} + IRQ_CONNECT({{ device.select_property('interrupts', irq_name, 'irq') }}, \ + {{ device.select_property('interrupts', irq_name, 'priority') }}, \ + {{ params.irq_func }}_{{ irq_name }}, \ + DEVICE_GET({{ gen_device_name(device) }}), \ + 0); + irq_enable({{ device.select_property('interrupts', irq_name, 'irq') }}); +{% endfor %} +{%- endmacro -%} + +{# Input parameters + data The data variable in the main template is not automatically carried over to this macro + compatibles Tell the macro which compatibels in the dts data to look for + params Is a list of parameters that this macro needs to render the code + init_priority_flag Defaults to the value "CONFIG_KERNEL_INIT_PRIORITY_DEVICE" if omitted + kernel_levels Defaults to the value "POST_KERNEL" if omitted + #} +{%- macro device(data, compatibles, params, init_priority_flag='CONFIG_KERNEL_INIT_PRIORITY_DEVICE', kernel_level='POST_KERNEL') -%} + +{# Will iterate through the compatibles list and look for device in dts data that + has the same compatibles string #} +{%- for compa in compatibles -%} + +{# Will copy the device reference in the compatibles lookup table in to a variable #} +{%- for device_id in codegen.edts().get_device_ids_by_compatible(compa) -%} + +{# Create a variable pointing to the devicetree metadata for the current intance #} +{%- set device = codegen.edts().get_device_by_device_id(device_id) %} + +{# Create some helper variable for information used multiple times, + can also be used by overloaded blocks #} +{%- set gen_variables = {'device_name' : gen_device_name(device), + 'config_struct_name' : gen_device_name(device) + "_config", + 'data_struct_name' : gen_device_name(device) + "_data", + 'irq_config_function_name' : gen_device_name(device) + "_config_irq", + 'device_label' : device.get_property('label')} -%} + + +#ifdef CONFIG_{{ gen_variables.device_label }} +#ifdef {{ params.irq_flag }} + +DEVICE_DECLARE({{ gen_variables.device_name }}); +static void {{ gen_variables.irq_config_function_name }}(struct device *dev) +{ +{{ gen_irq_connect(device, params) }} +} + +#endif + +{% if get_param(params, 'config_struct') != "None" -%} +static const struct {{ params.config_struct }} {{ gen_variables.config_struct_name }} = { +{{ codegen.render(params.config_struct_body, {'device':device, 'params':params, 'gen_variables':gen_variables }) }} +}; +{%- endif %} + +{% if get_param(params, 'data_struct') != "None" -%} +static struct {{ params.data_struct }} {{ gen_variables.data_struct_name }} = { +{%- if get_param(params, 'data_struct_body') != "None" -%} +{{ codegen.render(params.data_struct_body, {'device':device, 'params':params, 'gen_variables':gen_variables }) }} +{%- endif -%} +}; +{%- endif %} + +DEVICE_AND_API_INIT({{ gen_variables.device_name }}, \ + "{{ gen_variables.device_label }}", \ + &{{ params['init_function'] }}, \ + {{'NULL' if get_param(params, 'data_struct') == 'None' else '&'+gen_variables.data_struct_name }}, \ + {{'NULL' if get_param(params, 'config_struct') == 'None' else '&'+gen_variables.config_struct_name }}, \ + {{ kernel_level }}, \ + {{ init_priority_flag }}, \ + &{{ params['api_struct'] }}); + +#endif /* CONFIG_{{ gen_variables.device_label }} */ +{%- endfor -%} + +{%- endfor -%} + +{% endmacro %} diff --git a/templates/devicedeclare.py b/templates/devicedeclare.py new file mode 100644 index 000000000000..b77d9b43ee1b --- /dev/null +++ b/templates/devicedeclare.py @@ -0,0 +1,316 @@ +# Copyright (c) 2018 Linaro Limited +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 + +import pprint +import re +import cogeno + +from string import Template + +_device_and_api_init_tmpl = \ + 'DEVICE_AND_API_INIT( \\\n' + \ + '\t${device-name}, \\\n' + \ + '\t"${driver-name}", \\\n' + \ + '\t${device-init}, \\\n' + \ + '\t&${device-data}, \\\n' + \ + '\t&${device-config-info}, \\\n' + \ + '\t${device-level}, \\\n' + \ + '\t${device-prio}, \\\n' + \ + '\t&${device-api});' + +## +# Aliases for EDTS property paths. +_property_path_aliases = [ + ('reg/0/address/0', 'reg/address'), + ('reg/0/size/0', 'reg/size'), +] + +class _DeviceLocalTemplate(Template): + # pattern is ${} + # never starts with / + # extend default pattern by '-' '/' ',' + idpattern = r'[_a-z][_a-z0-9\-/,]*' + + +class _DeviceGlobalTemplate(Template): + # pattern is ${:} + # device ID is the same as node address + # always starts with / + # extend default pattern by '-', '@', '/', ':' + idpattern = r'/[_a-z0-9\-/,@:]*' + +## +# @brief Substitude values in device template +# +def _device_template_substitute(template, device_id, preset={}): + device = cogeno.edts().get_device_by_device_id(device_id) + # substitute device local placeholders ${}, config, ... + mapping = {} + # add preset mapping + mapping.update(preset) + # add device properties from device tree + mapping.update(device.get_properties_flattened()) + # add specific device declaration vars/ constants + mapping['device-name'] = mapping.get('device-name', device.get_name()) + mapping['driver-name'] = mapping.get('driver-name', + device.get_property('label').strip('"')) + mapping['device-data'] = mapping.get('device-data', + "{}_data".format(mapping['device-name']).lower()) + mapping['device-config-info'] = mapping.get('device-config-info', + "{}_config".format(mapping['device-name']).lower()) + mapping['device-config-irq'] = mapping.get('device-config-irq', + "{}_config_irq".format(mapping['device-name']).lower()) + substituted = _DeviceLocalTemplate(template).safe_substitute(mapping) + + # substitute device global placeholders ${:} + # + # we need a second substitude to allow for device indirections + # ${${}:} + mapping = {} + for device_id in cogeno.edts()['devices']: + path_prefix = device_id + ':' + device = cogeno.edts().get_device_by_device_id(device_id) + mapping.update(device.get_properties_flattened(path_prefix)) + # add specific device declaration vars/ constants + try: + mapping[path_prefix + 'device-name'] = device.get_name() + mapping[path_prefix + 'driver-name'] = \ + device.get_property('label').strip('"') + except: + # will be obvious if any of this is needed, just skip here + pass + + # add aliases to mapping + aliases_mapping = {} + for property_path, property_value in mapping.items(): + for alias_property_path, alias in _property_path_aliases: + if property_path.endswith(alias_property_path): + property_path = property_path[:-len(alias_property_path)] \ + + alias + aliases_mapping[property_path] = property_value + mapping.update(aliases_mapping) + + substituted = _DeviceGlobalTemplate(substituted).safe_substitute(mapping) + + return substituted + + +# +# @return True if device is declared, False otherwise +def device_declare_single(device_config, + driver_name, + device_init, + device_level, + device_prio, + device_api, + device_info, + device_defaults = {}): + device_configured = cogeno.config_property(device_config, '') + if device_configured == '' or device_configured[-1] == '0': + # Not configured - do not generate + # + # The generation decision must be taken by cogen here + # (vs. #define CONFIG_xxx) to cope with the following situation: + # + # If config is not set the device may also be not activated in the + # device tree. No device tree info is available in this case. + # An attempt to generate code without the DTS info + # will lead to an exception for a valid situation. + cogeno.outl("/* !!! '{}' not configured !!! */".format(driver_name)) + return False + + device_id = cogeno.edts().get_device_id_by_label(driver_name) + if device_id is None: + # this should not happen + raise cogeno.Error("Did not find driver name '{}'.".format(driver_name)) + + # Presets for mapping this device data to template + preset = device_defaults + preset['device-init'] = device_init + preset['device-level'] = device_level + preset['device-prio'] = device_prio + preset['device-api'] = device_api + preset['device-config'] = device_config + preset['driver-name'] = driver_name.strip('"') + + # + # device info + if device_info: + device_info = _device_template_substitute(device_info, device_id, + preset) + cogeno.outl(device_info) + # + # device init + cogeno.outl(_device_template_substitute(_device_and_api_init_tmpl, + device_id, preset)) + return True + +## +# @param device_configs +# A list of configuration variables for device instantiation. +# (e.g. ['CONFIG_SPI_0', 'CONFIG_SPI_1']) +# @param driver_names +# A list of driver names for device instantiation. The list shall be ordered +# as the list of device configs. +# (e.g. ['SPI_0', 'SPI_1']) +# @param device_inits +# A list of device initialisation functions or a one single function. The +# list shall be ordered as the list of device configs. +# (e.g. 'spi_stm32_init') +# @param device_levels +# A list of driver initialisation levels or one single level definition. The +# list shall be ordered as the list of device configs. +# (e.g. 'PRE_KERNEL_1') +# @param device_prios +# A list of driver initialisation priorities or one single priority +# definition. The list shall be ordered as the list of device configs. +# (e.g. 32) +# @param device_api +# Identifier of the device api. +# (e.g. 'spi_stm32_driver_api') +# @param device_info +# Device info template for device driver config, data and interrupt +# initialisation. +# @param device_defaults +# Device default property values. `device_defaults` is a dictionary of +# property path : property value. +# +def device_declare_multi(device_configs, + driver_names, + device_inits, + device_levels, + device_prios, + device_api, + device_info, + device_defaults = {}): + devices_declared = [] + for i, device_config in enumerate(device_configs): + driver_name = driver_names[i] + if isinstance(device_inits, str): + device_init = device_inits + else: + try: + device_init = device_inits[i] + except: + device_init = device_inits + if isinstance(device_levels, str): + device_level = device_levels + else: + try: + device_level = device_levels[i] + except: + device_level = device_levels + if isinstance(device_prios, str): + device_prio = device_prios + else: + try: + device_prio = device_prios[i] + except: + device_prio = device_prios + + device_declared = device_declare_single(device_config, + driver_name, + device_init, + device_level, + device_prio, + device_api, + device_info, + device_defaults) + devices_declared.append(str(device_declared)) + + if 'True' not in devices_declared: + err = "No active device found for {} = {} and {}.".format( + ', '.join(device_configs), ', '.join(devices_declared), + ', '.join(driver_names)) + cogeno.log(err) + raise cogeno.Error(err) + + +def _device_generate_struct(type_of_struct, _struct): + if _struct is None and type_of_struct == 'config': + return 'static const int ${device-config-info}[] = {};\n' + elif _struct is None and type_of_struct == 'data': + return 'static int ${device-data}[] = {};\n' + + struct = "" + # convert _struct into a list. Struct might have only one element + if type(_struct) is str: + _struct = [_struct] + else: + _struct = list(_struct) + + if type_of_struct == 'config': + struct += 'static const struct {} ${{device-config-info}} = {{\n'.format(_struct[0]) + elif type_of_struct == 'data': + struct += 'static struct {} ${{device-data}} = {{\n'.format(_struct[0]) + else: + msg("Not expected") + + if len(_struct) > 1: + struct += _struct[1] + + struct += '};\n\n' + return struct + + +def _device_generate_irq_bootstrap(irq_names, irq_flag, irq_func): + irq_bootstrap_info = \ + '#ifdef {}\n'.format(irq_flag) + \ + 'DEVICE_DECLARE(${device-name});\n' + \ + 'static void ${device-config-irq}(struct device *dev)\n' + \ + '{\n' + for irq_name in irq_names: + irq_num = '${{interrupts/{}/irq}}'.format(irq_name) + irq_prio = '${{interrupts/{}/priority}}'.format(irq_name) + irq_bootstrap_info += \ + '\tIRQ_CONNECT({},\n'.format(irq_num) + \ + '\t\t{},\n'.format(irq_prio) + if len(irq_names) == 1 and irq_name == '0': + # Only one irq and no name associated. Keep it simple name + irq_bootstrap_info += '\t\t{},\n'.format(irq_func) + else: + irq_bootstrap_info += '\t\t{}_{},\n'.format(irq_func, irq_name) + irq_bootstrap_info += \ + '\t\tDEVICE_GET(${device-name}),\n' + \ + '\t\t0);\n' + \ + '\tirq_enable({});\n\n'.format(irq_num) + irq_bootstrap_info += \ + '}\n' + \ + '#endif /* {} */\n\n'.format(irq_flag) + return irq_bootstrap_info + + +def device_declare(compatibles, init_prio_flag, kernel_level, irq_func, + init_func, api, data_struct, config_struct): + + config_struct = _device_generate_struct('config', config_struct) + data_struct = _device_generate_struct('data', data_struct) + if api is None: + api = "(*(const int *)0)" + + for device_id in cogeno.edts().get_device_ids_by_compatible(compatibles): + device = cogeno.edts().get_device_by_device_id(device_id) + driver_name = device.get_property('label') + device_config = "CONFIG_{}".format(driver_name.strip('"')) + interrupts = device.get_property('interrupts', None) + if interrupts is not None: + irq_names = list(interrupts.keys()) + else: + irq_names = None + + device_info = "" + if irq_func is not None: + device_info += _device_generate_irq_bootstrap( + irq_names, irq_func['irq_flag'], irq_func['irq_func']) + device_info += config_struct + device_info += data_struct + + device_declare_single(device_config, + driver_name, + init_func, + kernel_level, + init_prio_flag, + api, + device_info) From 0f9247dc842a9d2984c9b89ca6cafdd0e8f9cf5f Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 2 Nov 2018 17:25:20 +0100 Subject: [PATCH 05/13] scripts: integrate code generation into Zephyr build system Integrate code generation by cogeno into Zephyr build system. If a source file is added by zephyr_sources_codegen(..) or zephyr_sources_codegen_ifdef(..) cogeno will be executed and the generated file stored to the build directory. Compilation will run on the generated file only. Signed-off-by: Bobby Noelte --- CMakeLists.txt | 7 ++ cmake/extensions.cmake | 151 +++++++++++++++++++++++++++++++++++++++ scripts/gen_code.py | 73 +++++++++++++++++++ scripts/requirements.txt | 1 + 4 files changed, 232 insertions(+) create mode 100755 scripts/gen_code.py diff --git a/CMakeLists.txt b/CMakeLists.txt index bbb1e92c45aa..37c5251b887c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -412,6 +412,13 @@ add_subdirectory(subsys) add_subdirectory(drivers) add_subdirectory(tests) +# Add all generated zephyr sources in the same context as the zephyr library +# is created. Assure all sub directories that might invoke code generation +# are processed before. +get_property(zephyr_generated_sources GLOBAL PROPERTY zephyr_generated_sources_property) +set_source_files_properties(${zephyr_generated_sources} PROPERTIES GENERATED 1) +target_sources(zephyr PRIVATE ${zephyr_generated_sources}) + set(syscall_macros_h ${ZEPHYR_BINARY_DIR}/include/generated/syscall_macros.h) add_custom_target(syscall_macros_h_target DEPENDS ${syscall_macros_h}) diff --git a/cmake/extensions.cmake b/cmake/extensions.cmake index 3bbfd9304c88..54704ddeab8b 100644 --- a/cmake/extensions.cmake +++ b/cmake/extensions.cmake @@ -357,6 +357,13 @@ macro(zephyr_library_named name) # This is a macro because we need add_library() to be executed # within the scope of the caller. set(ZEPHYR_CURRENT_LIBRARY ${name}) + + if("${name}" STREQUAL "zephyr") + # We have to mark all the generated files "GENERATED" in this context + get_property(zephyr_generated_files GLOBAL PROPERTY zephyr_generated_files_property) + set_source_files_properties(${zephyr_generated_files} PROPERTIES GENERATED 1) + endif() + add_library(${name} STATIC "") zephyr_append_cmake_library(${name}) @@ -516,6 +523,150 @@ function(generate_inc_file_for_target generate_inc_file_for_gen_target(${target} ${source_file} ${generated_file} ${generated_target_name} ${ARGN}) endfunction() +function(target_sources_codegen + target # The cmake target that depends on the generated file + ) + + set(options) + set(oneValueArgs) + set(multiValueArgs CODEGEN_DEFINES DEPENDS) + cmake_parse_arguments(CODEGEN "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN}) + # prepend -D to all defines + string(REGEX REPLACE "([^;]+)" "-D;\\1" + CODEGEN_CODEGEN_DEFINES "${CODEGEN_CODEGEN_DEFINES}") + # Get all the files that make up cogeno for dependency reasons. + file(GLOB CODEGEN_SOURCES + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/*.py + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/*.py + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/extractors/*.py + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/devicetree/*.py + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/bindings/*.yaml + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/*.c + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/*.jinja2 + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/zephyr/*.c + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/zephyr/*.jinja2 + ${ZEPHYR_BASE}/scripts/cogeno/cogeno/*.py) + + message(STATUS "Will generate for target ${target}") + # Generated file must be generated to the current binary directory. + # Otherwise this would trigger CMake issue #14633: + # https://gitlab.kitware.com/cmake/cmake/issues/14633 + foreach(arg ${CODEGEN_UNPARSED_ARGUMENTS}) + if(IS_ABSOLUTE ${arg}) + set(template_file ${arg}) + get_filename_component(generated_file_name ${arg} NAME) + set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${generated_file_name}) + else() + set(template_file ${CMAKE_CURRENT_SOURCE_DIR}/${arg}) + set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${arg}) + endif() + get_filename_component(template_dir ${template_file} DIRECTORY) + get_filename_component(generated_dir ${generated_file} DIRECTORY) + + if(IS_DIRECTORY ${template_file}) + message(FATAL_ERROR "target_sources_codegen() was called on a directory") + endif() + + # Generate file from template + message(STATUS " from '${template_file}'") + message(STATUS " to '${generated_file}'") + add_custom_command( + COMMENT "codegen ${generated_file}" + OUTPUT ${generated_file} + MAIN_DEPENDENCY ${template_file} + DEPENDS + ${CODEGEN_DEPENDS} + ${CODEGEN_SOURCES} + COMMAND + ${PYTHON_EXECUTABLE} + ${ZEPHYR_BASE}/scripts/gen_code.py + ${CODEGEN_CODEGEN_DEFINES} + -D "\"BOARD=${BOARD}\"" + -D "\"APPLICATION_SOURCE_DIR=${APPLICATION_SOURCE_DIR}\"" + -D "\"APPLICATION_BINARY_DIR=${APPLICATION_BINARY_DIR}\"" + -D "\"PROJECT_NAME=${PROJECT_NAME}\"" + -D "\"PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}\"" + -D "\"PROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}\"" + -D "\"CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}\"" + -D "\"CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}\"" + -D "\"CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}\"" + -D "\"CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}\"" + -D "\"CMAKE_CURRENT_LIST_DIR=${CMAKE_CURRENT_LIST_DIR}\"" + -D "\"CMAKE_FILES_DIRECTORY=${CMAKE_FILES_DIRECTORY}\"" + -D "\"CMAKE_PROJECT_NAME=${CMAKE_PROJECT_NAME}\"" + -D "\"CMAKE_SYSTEM=${CMAKE_SYSTEM}\"" + -D "\"CMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}\"" + -D "\"CMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}\"" + -D "\"CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}\"" + -D "\"CMAKE_C_COMPILER=${CMAKE_C_COMPILER}\"" + -D "\"CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\"" + -D "\"CMAKE_COMPILER_IS_GNUCC=${CMAKE_COMPILER_IS_GNUCC}\"" + -D "\"CMAKE_COMPILER_IS_GNUCXX=${CMAKE_COMPILER_IS_GNUCXX}\"" + -D "\"GENERATED_DTS_BOARD_H=${GENERATED_DTS_BOARD_H}\"" + -D "\"GENERATED_DTS_BOARD_CONF=${GENERATED_DTS_BOARD_CONF}\"" + --config "${PROJECT_BINARY_DIR}/.config" + --cmakecache "${CMAKE_BINARY_DIR}/CMakeCache.txt" + --dts "${PROJECT_BINARY_DIR}/${BOARD}.dts_compiled" + --bindings "${DTS_APP_BINDINGS}" "${PROJECT_SOURCE_DIR}/dts/bindings" + --edts "${PROJECT_BINARY_DIR}/edts.json" + --modules "${APPLICATION_SOURCE_DIR}/templates" "${PROJECT_SOURCE_DIR}/templates" + --templates "${APPLICATION_SOURCE_DIR}/templates" "${PROJECT_SOURCE_DIR}/templates" + --input "${template_file}" + --output "${generated_file}" + --log "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/codegen.log" + --lock "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/codegen.lock" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + if("${target}" STREQUAL "zephyr") + # zephyr is special + generate_unique_target_name_from_filename(${generated_file} generated_target_name) + add_custom_target(${generated_target_name} DEPENDS ${generated_file}) + add_dependencies(zephyr ${generated_target_name}) + # Remember all the files that are generated for zephyr. + # target_sources(zephyr PRIVATE ${zephyr_generated_sources}) + # is executed in the top level CMakeFile.txt context. + get_property(zephyr_generated_sources GLOBAL PROPERTY zephyr_generated_sources_property) + list(APPEND zephyr_generated_sources ${generated_file}) + set_property(GLOBAL PROPERTY zephyr_generated_sources_property "${zephyr_generated_sources}") + # Add template directory to include path to allow includes with + # relative path in generated file to work + zephyr_include_directories(${template_dir}) + # Add directory of generated file to include path to allow includes + # of generated header file with relative path. + zephyr_include_directories(${generated_dir}) + else() + target_sources(${target} PRIVATE ${generated_file}) + # Add template directory to include path to allow includes with + # relative path in generated file to work + target_include_directories(${target} PRIVATE ${template_dir}) + # Add directory of generated file to include path to allow includes + # of generated header file with relative path. + target_include_directories(${target} PRIVATE ${generated_dir}) + endif() + endforeach() +endfunction() + +function(zephyr_sources_codegen) + target_sources_codegen(zephyr ${ARGN}) +endfunction() + +function(zephyr_sources_codegen_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_sources_codegen(${ARGN}) + endif() +endfunction() + +function(zephyr_library_sources_codegen) + target_sources_codegen(${ZEPHYR_CURRENT_LIBRARY} ${ARGN}) +endfunction() + +function(zephyr_library_sources_codegen_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_library_sources_codegen(${ARGN}) + endif() +endfunction() + # 1.4. board_* # # This section is for extensions which control Zephyr's board runners diff --git a/scripts/gen_code.py b/scripts/gen_code.py new file mode 100755 index 000000000000..258454be8a2c --- /dev/null +++ b/scripts/gen_code.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2018 Bobby Noelte. +# +# SPDX-License-Identifier: Apache-2.0 + +# Zephyr 'cogeno' launcher which is interoperable with: +# +# 1. "multi-repo" Zephyr installations where 'cogeno' is provided in a +# separate Git repository side by side to Zephyr. +# +# 2. Zephyr installations that have 'cogeno' installed by PyPi. +# +# 3. "mono-repo" Zephyr installations that have 'cogeno' supplied by a copy +# in scripts/cogeno. +# + +import sys +import subprocess +import importlib.util +from pathlib import Path + + +if sys.version_info < (3,): + sys.exit('fatal error: you are running Python 2') + +# 1. - cogeno git repository side by side to zephyr + +zephyr_path = Path(__file__).resolve().parents[1] + +cogeno_module_dir = zephyr_path.parent.joinpath('cogeno') + +if cogeno_module_dir.is_dir(): + cogeno_module_dir_s = str(cogeno_module_dir) + sys.path.insert(0, cogeno_module_dir_s) + try: + from cogeno.cogeno import main + try: + desc = check_output(['git', 'describe', '--tags'], + stderr=DEVNULL, + cwd=str(cogeno_module_dir)) + cogeno_version = desc.decode(sys.getdefaultencoding()).strip() + except: + cogeno_version = 'unknown' + cogeno_path = cogeno_module_dir.joinpath('cogeno') + print("NOTE: you are running cogeno from '{}' ({});".format(cogeno_path, cogeno_version)) + except: + cogeno_module_dir = None + finally: + sys.path.remove(cogeno_module_dir_s) +else: + cogeno_module_dir = None + +if cogeno_module_dir is None: + cogeno_spec = importlib.util.find_spec('cogeno') + if cogeno_spec is None: + print("Error: can not find cogeno;") + elif 'scripts/cogeno' in cogeno_spec.submodule_search_locations._path[0]: + # 3. - use cogeno copy in scripts directory + from cogeno.cogeno.cogeno import main + cogeno_path = Path(__file__).resolve().parent.joinpath('cogeno/cogeno') + print("NOTE: you are running a copy of cogeno from '{}';". + format(cogeno_path), + "this may be removed from the Zephpyr repository in the future.", + "Cogeno is now developed separately.") + else: + # 2. - cogeno already installed by pip + from cogeno.cogeno import main + print("NOTE: you are running cogeno from a local installation;") + +if __name__ == '__main__': + ret = main() + sys.exit(ret) diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 463c8b82d6d4..98ecbb289c97 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -13,6 +13,7 @@ pyelftools==0.24 pyocd==0.12.0 pyserial pykwalify +Jinja2 # "win32" is used for 64-bit Windows as well windows-curses; sys_platform == "win32" colorama From fee96d1a9790c28f160f4217fcf9e3473e192f95 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 27 Jul 2018 15:51:47 +0200 Subject: [PATCH 06/13] drivers: i2c: stm32: use code generation for driver instantiation Replace device instances code by codegen solution. Signed-off-by: Bobby Noelte --- drivers/i2c/CMakeLists.txt | 13 +- drivers/i2c/i2c_ll_stm32.c | 314 ++++++++++++------------------------- drivers/i2c/i2c_ll_stm32.h | 4 + 3 files changed, 109 insertions(+), 222 deletions(-) diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index 6eff42c950ff..dbbf4e8d30df 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -18,14 +18,11 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SBCON i2c_sbcon.c) zephyr_library_sources_ifdef(CONFIG_I2C_NIOS2 i2c_nios2.c) zephyr_library_sources_ifdef(CONFIG_I2C_GECKO i2c_gecko.c) -zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 - i2c_ll_stm32_v1.c - i2c_ll_stm32.c - ) -zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 - i2c_ll_stm32_v2.c - i2c_ll_stm32.c - ) +zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32_v1.c) +zephyr_library_sources_codegen_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32.c) + +zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_v2.c) +zephyr_library_sources_codegen_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE i2c_handlers.c) diff --git a/drivers/i2c/i2c_ll_stm32.c b/drivers/i2c/i2c_ll_stm32.c index 821260de6b0c..f00f0a7c683f 100644 --- a/drivers/i2c/i2c_ll_stm32.c +++ b/drivers/i2c/i2c_ll_stm32.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2016 BayLibre, SAS * Copyright (c) 2017 Linaro Ltd + * Copyright (c) 2018 Bobby Noelte * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +21,23 @@ LOG_MODULE_REGISTER(i2c_ll_stm32); #include "i2c-priv.h" +#if defined(CONFIG_SOC_SERIES_STM32F3X) || defined(CONFIG_SOC_SERIES_STM32F0X) +/* + * STM32F0/3 I2C's independent clock source supports only + * HSI and SYSCLK, not APB1. We force I2C clock source to SYSCLK. + * I2C2 on STM32F0 uses APB1 clock as I2C clock source + */ +#define LL_I2C_NO_CLOCKSOURCE 0x12345678 /* dummy value */ +#define LL_I2C_1_CLOCKSOURCE LL_RCC_I2C1_CLKSOURCE_SYSCLK +#if defined(CONFIG_SOC_SERIES_STM32F3X) +#define LL_I2C_2_CLOCKSOURCE LL_RCC_I2C2_CLKSOURCE_SYSCLK +#else +/* I2C2 on STM32F0 uses APB1 clock - can't set clock source */ +#define LL_I2C_2_CLOCKSOURCE LL_I2C_NO_CLOCKSOURCE +#endif +#define LL_I2C_3_CLOCKSOURCE LL_RCC_I2C3_CLKSOURCE_SYSCLK +#endif + int i2c_stm32_runtime_configure(struct device *dev, u32_t config) { const struct i2c_stm32_config *cfg = DEV_CFG(dev); @@ -28,18 +46,24 @@ int i2c_stm32_runtime_configure(struct device *dev, u32_t config) u32_t clock = 0; #if defined(CONFIG_SOC_SERIES_STM32F3X) || defined(CONFIG_SOC_SERIES_STM32F0X) - LL_RCC_ClocksTypeDef rcc_clocks; - - /* - * STM32F0/3 I2C's independent clock source supports only - * HSI and SYSCLK, not APB1. We force clock variable to - * SYSCLK frequency. - */ - LL_RCC_GetSystemClocksFreq(&rcc_clocks); - clock = rcc_clocks.SYSCLK_Frequency; + if (cfg->ll_clock_source != LL_I2C_NO_CLOCKSOURCE) { + LL_RCC_ClocksTypeDef rcc_clocks; + + /* + * STM32F0/3 I2C's independent clock source supports only + * HSI and SYSCLK, not APB1. We force clock variable to + * SYSCLK frequency. + */ + LL_RCC_GetSystemClocksFreq(&rcc_clocks); + clock = rcc_clocks.SYSCLK_Frequency; + } else + /* I2C2 on STM32F0 uses APB1 clock as I2C clock source */ #else - clock_control_get_rate(device_get_binding(STM32_CLOCK_CONTROL_NAME), + { + clock_control_get_rate( + device_get_binding(STM32_CLOCK_CONTROL_NAME), (clock_control_subsys_t *) &cfg->pclken, &clock); + } #endif /* CONFIG_SOC_SERIES_STM32F3X) || CONFIG_SOC_SERIES_STM32F0X */ data->dev_config = config; @@ -173,32 +197,10 @@ static int i2c_stm32_init(struct device *dev) clock_control_on(clock, (clock_control_subsys_t *) &cfg->pclken); #if defined(CONFIG_SOC_SERIES_STM32F3X) || defined(CONFIG_SOC_SERIES_STM32F0X) - /* - * STM32F0/3 I2C's independent clock source supports only - * HSI and SYSCLK, not APB1. We force I2C clock source to SYSCLK. - * I2C2 on STM32F0 uses APB1 clock as I2C clock source - */ - - switch ((u32_t)cfg->i2c) { -#ifdef CONFIG_I2C_1 - case DT_I2C_1_BASE_ADDRESS: - LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_SYSCLK); - break; -#endif /* CONFIG_I2C_1 */ - -#if defined(CONFIG_SOC_SERIES_STM32F3X) && defined(CONFIG_I2C_2) - case DT_I2C_2_BASE_ADDRESS: - LL_RCC_SetI2CClockSource(LL_RCC_I2C2_CLKSOURCE_SYSCLK); - break; -#endif /* CONFIG_SOC_SERIES_STM32F3X && CONFIG_I2C_2 */ - -#ifdef CONFIG_I2C_3 - case DT_I2C_3_BASE_ADDRESS: - LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_SYSCLK); - break; -#endif /* CONFIG_I2C_3 */ + if (cfg->ll_clock_source != LL_I2C_NO_CLOCKSOURCE) { + LL_RCC_SetI2CClockSource(cfg->ll_clock_source); } -#endif /* CONFIG_SOC_SERIES_STM32F3X) || CONFIG_SOC_SERIES_STM32F0X */ +#endif bitrate_cfg = _i2c_map_dt_bitrate(cfg->bitrate); @@ -211,182 +213,66 @@ static int i2c_stm32_init(struct device *dev) return 0; } -#ifdef CONFIG_I2C_1 - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_1(struct device *port); -#endif - -static const struct i2c_stm32_config i2c_stm32_cfg_1 = { - .i2c = (I2C_TypeDef *)DT_I2C_1_BASE_ADDRESS, - .pclken = { - .enr = DT_I2C_1_CLOCK_BITS, - .bus = DT_I2C_1_CLOCK_BUS, - }, -#ifdef CONFIG_I2C_STM32_INTERRUPT - .irq_config_func = i2c_stm32_irq_config_func_1, -#endif - .bitrate = DT_I2C_1_BITRATE, -}; - -static struct i2c_stm32_data i2c_stm32_dev_data_1; - -DEVICE_AND_API_INIT(i2c_stm32_1, CONFIG_I2C_1_NAME, &i2c_stm32_init, - &i2c_stm32_dev_data_1, &i2c_stm32_cfg_1, - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &api_funcs); - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_1(struct device *dev) -{ -#ifdef CONFIG_I2C_STM32_COMBINED_INTERRUPT - IRQ_CONNECT(DT_I2C_1_COMBINED_IRQ, DT_I2C_1_COMBINED_IRQ_PRI, - stm32_i2c_combined_isr, DEVICE_GET(i2c_stm32_1), 0); - irq_enable(DT_I2C_1_COMBINED_IRQ); -#else - IRQ_CONNECT(DT_I2C_1_EVENT_IRQ, DT_I2C_1_EVENT_IRQ_PRI, - stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_1), 0); - irq_enable(DT_I2C_1_EVENT_IRQ); - - IRQ_CONNECT(DT_I2C_1_ERROR_IRQ, DT_I2C_1_ERROR_IRQ_PRI, - stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_1), 0); - irq_enable(DT_I2C_1_ERROR_IRQ); -#endif -} -#endif - -#endif /* CONFIG_I2C_1 */ - -#ifdef CONFIG_I2C_2 - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_2(struct device *port); -#endif - -static const struct i2c_stm32_config i2c_stm32_cfg_2 = { - .i2c = (I2C_TypeDef *)DT_I2C_2_BASE_ADDRESS, - .pclken = { - .enr = DT_I2C_2_CLOCK_BITS, - .bus = DT_I2C_2_CLOCK_BUS, - }, -#ifdef CONFIG_I2C_STM32_INTERRUPT - .irq_config_func = i2c_stm32_irq_config_func_2, -#endif - .bitrate = DT_I2C_2_BITRATE, -}; - -static struct i2c_stm32_data i2c_stm32_dev_data_2; - -DEVICE_AND_API_INIT(i2c_stm32_2, CONFIG_I2C_2_NAME, &i2c_stm32_init, - &i2c_stm32_dev_data_2, &i2c_stm32_cfg_2, - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &api_funcs); - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_2(struct device *dev) -{ -#ifdef CONFIG_I2C_STM32_COMBINED_INTERRUPT - IRQ_CONNECT(DT_I2C_2_COMBINED_IRQ, DT_I2C_2_COMBINED_IRQ_PRI, - stm32_i2c_combined_isr, DEVICE_GET(i2c_stm32_2), 0); - irq_enable(DT_I2C_2_COMBINED_IRQ); -#else - IRQ_CONNECT(DT_I2C_2_EVENT_IRQ, DT_I2C_2_EVENT_IRQ_PRI, - stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_2), 0); - irq_enable(DT_I2C_2_EVENT_IRQ); - - IRQ_CONNECT(DT_I2C_2_ERROR_IRQ, DT_I2C_2_ERROR_IRQ_PRI, - stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_2), 0); - irq_enable(DT_I2C_2_ERROR_IRQ); -#endif -} -#endif - -#endif /* CONFIG_I2C_2 */ - -#ifdef CONFIG_I2C_3 - -#ifndef I2C3_BASE -#error "I2C_3 is not available on the platform that you selected" -#endif /* I2C3_BASE */ - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_3(struct device *port); -#endif - -static const struct i2c_stm32_config i2c_stm32_cfg_3 = { - .i2c = (I2C_TypeDef *)DT_I2C_3_BASE_ADDRESS, - .pclken = { - .enr = DT_I2C_3_CLOCK_BITS, - .bus = DT_I2C_3_CLOCK_BUS, - }, -#ifdef CONFIG_I2C_STM32_INTERRUPT - .irq_config_func = i2c_stm32_irq_config_func_3, -#endif - .bitrate = DT_I2C_3_BITRATE, -}; - -static struct i2c_stm32_data i2c_stm32_dev_data_3; - -DEVICE_AND_API_INIT(i2c_stm32_3, CONFIG_I2C_3_NAME, &i2c_stm32_init, - &i2c_stm32_dev_data_3, &i2c_stm32_cfg_3, - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &api_funcs); - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_3(struct device *dev) -{ - IRQ_CONNECT(DT_I2C_3_EVENT_IRQ, DT_I2C_3_EVENT_IRQ_PRI, - stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_3), 0); - irq_enable(DT_I2C_3_EVENT_IRQ); - - IRQ_CONNECT(DT_I2C_3_ERROR_IRQ, DT_I2C_3_ERROR_IRQ_PRI, - stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_3), 0); - irq_enable(DT_I2C_3_ERROR_IRQ); -} -#endif - -#endif /* CONFIG_I2C_3 */ - -#ifdef CONFIG_I2C_4 - -#ifndef I2C4_BASE -#error "I2C_4 is not available on the platform that you selected" -#endif /* I2C4_BASE */ - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_4(struct device *port); -#endif - -static const struct i2c_stm32_config i2c_stm32_cfg_4 = { - .i2c = (I2C_TypeDef *)DT_I2C_4_BASE_ADDRESS, - .pclken = { - .enr = DT_I2C_4_CLOCK_BITS, - .bus = DT_I2C_4_CLOCK_BUS, - }, -#ifdef CONFIG_I2C_STM32_INTERRUPT - .irq_config_func = i2c_stm32_irq_config_func_4, -#endif - .bitrate = DT_I2C_4_BITRATE, -}; - -static struct i2c_stm32_data i2c_stm32_dev_data_4; - -DEVICE_AND_API_INIT(i2c_stm32_4, CONFIG_I2C_4_NAME, &i2c_stm32_init, - &i2c_stm32_dev_data_4, &i2c_stm32_cfg_4, - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &api_funcs); - -#ifdef CONFIG_I2C_STM32_INTERRUPT -static void i2c_stm32_irq_config_func_4(struct device *dev) -{ - IRQ_CONNECT(DT_I2C_4_EVENT_IRQ, DT_I2C_4_EVENT_IRQ_PRI, - stm32_i2c_event_isr, DEVICE_GET(i2c_stm32_4), 0); - irq_enable(DT_I2C_4_EVENT_IRQ); - - IRQ_CONNECT(DT_I2C_4_ERROR_IRQ, DT_I2C_4_ERROR_IRQ_PRI, - stm32_i2c_error_isr, DEVICE_GET(i2c_stm32_4), 0); - irq_enable(DT_I2C_4_ERROR_IRQ); -} -#endif - -#endif /* CONFIG_I2C_4 */ +/** + * @code{.cogeno.py} + * cogeno.import_module('devicedeclare') + * + * device_configs = ['CONFIG_I2C_{}'.format(x) for x in range(0, 5)] + * driver_names = ['I2C_{}'.format(x) for x in range(0, 5)] + * device_inits = 'i2c_stm32_init' + * device_levels = 'POST_KERNEL' + * device_prios = 'CONFIG_KERNEL_INIT_PRIORITY_DEVICE' + * device_api = 'api_funcs' + * device_info = \ + * """ + * #if CONFIG_I2C_STM32_INTERRUPT + * DEVICE_DECLARE(${device-name}); + * static void ${device-config-irq}(struct device *dev) + * { + * #ifdef CONFIG_I2C_STM32_COMBINED_INTERRUPT + * IRQ_CONNECT(${interrupts/combined/irq}, \\ + * ${interrupts/combined/priority}, \\ + * stm32_i2c_combined_isr, \\ + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/combined/irq}); + * #else + * IRQ_CONNECT(${interrupts/event/irq}, \\ + * ${interrupts/event/priority}, \\ + * stm32_i2c_event_isr, \\ + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/event/irq}); + * IRQ_CONNECT(${interrupts/error/irq}, \\ + * ${interrupts/error/priority}, \\ + * stm32_i2c_error_isr, \\ + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/error/irq}); + * #endif + * } + * #endif + * static const struct i2c_stm32_config ${device-config-info} = { + * .i2c = (I2C_TypeDef *)${reg/0/address/0}, + * .pclken.bus = ${clocks/0/bus}, + * .pclken.enr = ${clocks/0/bits}, + * #if CONFIG_I2C_STM32_INTERRUPT + * .irq_config_func = ${device-config-irq}, + * #endif + * .bitrate = ${clock-frequency}, + * #if defined(CONFIG_SOC_SERIES_STM32F3X) \\ + * || defined(CONFIG_SOC_SERIES_STM32F0X) + * .ll_clock_source = LL_${driver-name}_CLOCKSOURCE, + * #endif + * }; + * static struct i2c_stm32_data ${device-data}; + * """ + * + * devicedeclare.device_declare_multi( \ + * device_configs, + * driver_names, + * device_inits, + * device_levels, + * device_prios, + * device_api, + * device_info) + * @endcode{.cogeno.py} + */ +/** @code{.cogeno.ins}@endcode */ diff --git a/drivers/i2c/i2c_ll_stm32.h b/drivers/i2c/i2c_ll_stm32.h index 528064f56d9b..fff5f643d5f3 100644 --- a/drivers/i2c/i2c_ll_stm32.h +++ b/drivers/i2c/i2c_ll_stm32.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2016 BayLibre, SAS * Copyright (c) 2017 Linaro Ltd + * Copyright (c) 2018 Bobby Noelte * * SPDX-License-Identifier: Apache-2.0 * @@ -18,6 +19,9 @@ struct i2c_stm32_config { struct stm32_pclken pclken; I2C_TypeDef *i2c; u32_t bitrate; +#if defined(CONFIG_SOC_SERIES_STM32F3X) || defined(CONFIG_SOC_SERIES_STM32F0X) + u32_t ll_clock_source; +#endif }; struct i2c_stm32_data { From 320a8f23e949862aca6e08868b199ae3251c2577 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 2 Feb 2018 09:18:19 +0100 Subject: [PATCH 07/13] drivers: spi: stm32: use code generation for driver instantiation Replace device instances code by code generation solution. For testing purposes all different ways of code generation are done for this driver: - inline template - cogeno and Jinja2 snippets - generates a source file - Jinja2 template - generates a header file - codegen template - generates a header file Compilation is done for the unmodified source file including the codegen generated header file. Additionally the following adaptations are done: - Extend bindings to allow DTS extract for device info. - Correct DTS to be used in SPI driver. Signed-off-by: Bobby Noelte --- drivers/spi/CMakeLists.txt | 4 +- drivers/spi/Kconfig.stm32 | 2 +- drivers/spi/spi_ll_stm32.c | 238 +++++++++++------------ drivers/spi/spi_ll_stm32.h | 2 +- drivers/spi/spi_ll_stm32_jinja2.h.jinja2 | 26 +++ drivers/spi/spi_ll_stm32_python.h.py | 42 ++++ dts/arm/st/f0/stm32f0.dtsi | 2 +- dts/bindings/spi/spi.yaml | 7 +- dts/bindings/spi/st,stm32-spi-fifo.yaml | 6 + 9 files changed, 204 insertions(+), 125 deletions(-) create mode 100644 drivers/spi/spi_ll_stm32_jinja2.h.jinja2 create mode 100644 drivers/spi/spi_ll_stm32_python.h.py diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index c626a1409c32..3b2752ad604b 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -2,7 +2,9 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_SPI_DW spi_dw.c) zephyr_library_sources_ifdef(CONFIG_SPI_INTEL spi_intel.c) -zephyr_library_sources_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c) +zephyr_library_sources_codegen_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c) +zephyr_library_sources_codegen_ifdef(CONFIG_SPI_STM32 spi_ll_stm32_python.h.py) +zephyr_library_sources_codegen_ifdef(CONFIG_SPI_STM32 spi_ll_stm32_jinja2.h.jinja2) zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_DSPI spi_mcux_dspi.c) zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_LPSPI spi_mcux_lpspi.c) zephyr_library_sources_ifdef(CONFIG_SPI_SAM spi_sam.c) diff --git a/drivers/spi/Kconfig.stm32 b/drivers/spi/Kconfig.stm32 index 5a1666daff7f..f6f993a7e342 100644 --- a/drivers/spi/Kconfig.stm32 +++ b/drivers/spi/Kconfig.stm32 @@ -8,7 +8,7 @@ menuconfig SPI_STM32 bool "STM32 MCU SPI controller driver" - depends on SPI && SOC_FAMILY_STM32 + default y if SPI && SOC_FAMILY_STM32 select USE_STM32_LL_SPI help Enable SPI support on the STM32 family of processors. diff --git a/drivers/spi/spi_ll_stm32.c b/drivers/spi/spi_ll_stm32.c index 3d70d4391d60..fe2ccea4a4d2 100644 --- a/drivers/spi/spi_ll_stm32.c +++ b/drivers/spi/spi_ll_stm32.c @@ -1,19 +1,37 @@ /* * Copyright (c) 2016 BayLibre, SAS + * Copyright (c) 2018 Bobby Noelte * * SPDX-License-Identifier: Apache-2.0 */ +/** @file + * @brief STM32 SPI implementation + * @defgroup device_driver_spi_stm32 STM32 SPI inplementation + * + * A common driver for STM32 SPI. SoC specific adaptions are + * done by device tree and soc.h. + * + * @{ + */ + +#include +#include +#include + +#include + #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL #include LOG_MODULE_REGISTER(spi_ll_stm32); #include -#include #include +#include #include +#include +#include #include -#include #include #include @@ -338,12 +356,12 @@ static int spi_stm32_configure(struct device *dev, spi_context_cs_configure(&data->ctx); LOG_DBG("Installed config %p: freq %uHz (div = %u)," - " mode %u/%u/%u, slave %u", - config, clock >> br, 1 << br, - (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) ? 1 : 0, - (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) ? 1 : 0, - (SPI_MODE_GET(config->operation) & SPI_MODE_LOOP) ? 1 : 0, - config->slave); + " mode %u/%u/%u, slave %u", + config, clock >> br, 1 << br, + (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) ? 1 : 0, + (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) ? 1 : 0, + (SPI_MODE_GET(config->operation) & SPI_MODE_LOOP) ? 1 : 0, + config->slave); return 0; } @@ -450,7 +468,7 @@ static int spi_stm32_transceive_async(struct device *dev, } #endif /* CONFIG_SPI_ASYNC */ -static const struct spi_driver_api api_funcs = { +static const struct spi_driver_api spi_stm32_driver_api = { .transceive = spi_stm32_transceive, #ifdef CONFIG_SPI_ASYNC .transceive_async = spi_stm32_transceive_async, @@ -469,7 +487,7 @@ static int spi_stm32_init(struct device *dev) (clock_control_subsys_t) &cfg->pclken); #ifdef CONFIG_SPI_STM32_INTERRUPT - cfg->irq_config(dev); + cfg->config_irq(dev); #endif spi_context_unlock_unconditionally(&data->ctx); @@ -477,116 +495,98 @@ static int spi_stm32_init(struct device *dev) return 0; } -#ifdef CONFIG_SPI_1 - -#ifdef CONFIG_SPI_STM32_INTERRUPT -static void spi_stm32_irq_config_func_1(struct device *port); -#endif - -static const struct spi_stm32_config spi_stm32_cfg_1 = { - .spi = (SPI_TypeDef *) DT_SPI_1_BASE_ADDRESS, - .pclken = { - .enr = DT_SPI_1_CLOCK_BITS, - .bus = DT_SPI_1_CLOCK_BUS - }, -#ifdef CONFIG_SPI_STM32_INTERRUPT - .irq_config = spi_stm32_irq_config_func_1, -#endif -}; - -static struct spi_stm32_data spi_stm32_dev_data_1 = { - SPI_CONTEXT_INIT_LOCK(spi_stm32_dev_data_1, ctx), - SPI_CONTEXT_INIT_SYNC(spi_stm32_dev_data_1, ctx), -}; - -DEVICE_AND_API_INIT(spi_stm32_1, DT_SPI_1_NAME, &spi_stm32_init, - &spi_stm32_dev_data_1, &spi_stm32_cfg_1, - POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, - &api_funcs); - -#ifdef CONFIG_SPI_STM32_INTERRUPT -static void spi_stm32_irq_config_func_1(struct device *dev) -{ - IRQ_CONNECT(DT_SPI_1_IRQ, DT_SPI_1_IRQ_PRI, - spi_stm32_isr, DEVICE_GET(spi_stm32_1), 0); - irq_enable(DT_SPI_1_IRQ); -} -#endif - -#endif /* CONFIG_SPI_1 */ - -#ifdef CONFIG_SPI_2 - -#ifdef CONFIG_SPI_STM32_INTERRUPT -static void spi_stm32_irq_config_func_2(struct device *port); -#endif - -static const struct spi_stm32_config spi_stm32_cfg_2 = { - .spi = (SPI_TypeDef *) DT_SPI_2_BASE_ADDRESS, - .pclken = { - .enr = DT_SPI_2_CLOCK_BITS, - .bus = DT_SPI_2_CLOCK_BUS - }, -#ifdef CONFIG_SPI_STM32_INTERRUPT - .irq_config = spi_stm32_irq_config_func_2, -#endif -}; - -static struct spi_stm32_data spi_stm32_dev_data_2 = { - SPI_CONTEXT_INIT_LOCK(spi_stm32_dev_data_2, ctx), - SPI_CONTEXT_INIT_SYNC(spi_stm32_dev_data_2, ctx), -}; - -DEVICE_AND_API_INIT(spi_stm32_2, DT_SPI_2_NAME, &spi_stm32_init, - &spi_stm32_dev_data_2, &spi_stm32_cfg_2, - POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, - &api_funcs); - -#ifdef CONFIG_SPI_STM32_INTERRUPT -static void spi_stm32_irq_config_func_2(struct device *dev) -{ - IRQ_CONNECT(DT_SPI_2_IRQ, DT_SPI_2_IRQ_PRI, - spi_stm32_isr, DEVICE_GET(spi_stm32_2), 0); - irq_enable(DT_SPI_2_IRQ); -} -#endif - -#endif /* CONFIG_SPI_2 */ - -#ifdef CONFIG_SPI_3 - -#ifdef CONFIG_SPI_STM32_INTERRUPT -static void spi_stm32_irq_config_func_3(struct device *port); -#endif - -static const struct spi_stm32_config spi_stm32_cfg_3 = { - .spi = (SPI_TypeDef *) DT_SPI_3_BASE_ADDRESS, - .pclken = { - .enr = DT_SPI_3_CLOCK_BITS, - .bus = DT_SPI_3_CLOCK_BUS - }, -#ifdef CONFIG_SPI_STM32_INTERRUPT - .irq_config = spi_stm32_irq_config_func_3, +#if 0 +#include "spi_ll_stm32_jinja2.h" +#else +#include "spi_ll_stm32_python.h" #endif -}; -static struct spi_stm32_data spi_stm32_dev_data_3 = { - SPI_CONTEXT_INIT_LOCK(spi_stm32_dev_data_3, ctx), - SPI_CONTEXT_INIT_SYNC(spi_stm32_dev_data_3, ctx), -}; - -DEVICE_AND_API_INIT(spi_stm32_3, DT_SPI_3_NAME, &spi_stm32_init, - &spi_stm32_dev_data_3, &spi_stm32_cfg_3, - POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, - &api_funcs); +#if 0 +/** + * For comparison and testing of + * - Codegen inline templates and + * - Jinja2 inline templates + * + * !!!!!!!!!!!!!!!!!!!!!! Testing Only !!!!!!!!!!!!!!!!!!!!!!! + */ -#ifdef CONFIG_SPI_STM32_INTERRUPT -static void spi_stm32_irq_config_func_3(struct device *dev) -{ - IRQ_CONNECT(DT_SPI_3_IRQ, DT_SPI_3_IRQ_PRI, - spi_stm32_isr, DEVICE_GET(spi_stm32_3), 0); - irq_enable(DT_SPI_3_IRQ); -} -#endif +/** + * @code{.cogeno.py} + * cogeno.import_module('devicedeclare') + * + * device_configs = ['CONFIG_SPI_{}'.format(x) for x in range(1, 7)] + * driver_names = ['SPI_{}'.format(x) for x in range(1, 7)] + * device_inits = 'spi_stm32_init' + * device_levels = 'POST_KERNEL' + * device_prios = 'CONFIG_SPI_INIT_PRIORITY' + * device_api = 'spi_stm32_driver_api' + * device_info = \ + * """ + * #if CONFIG_SPI_STM32_INTERRUPT + * DEVICE_DECLARE(${device-name}); + * static void ${device-config-irq}(struct device *dev) + * { + * IRQ_CONNECT(${interrupts/0/irq}, ${interrupts/0/priority}, \\ + * spi_stm32_isr, \\ + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/0/irq}); + * } + * #endif + * static const struct spi_stm32_config ${device-config-info} = { + * .spi = (SPI_TypeDef *)${reg/0/address/0}, + * .pclken.bus = ${clocks/0/bus}, + * .pclken.enr = ${clocks/0/bits}, + * #if CONFIG_SPI_STM32_INTERRUPT + * .config_irq = ${device-config-irq}, + * #endif + * }; + * static struct spi_stm32_data ${device-data} = { + * SPI_CONTEXT_INIT_LOCK(${device-data}, ctx), + * SPI_CONTEXT_INIT_SYNC(${device-data}, ctx), + * }; + * """ + * + * devicedeclare.device_declare_multi( \ + * device_configs, + * driver_names, + * device_inits, + * device_levels, + * device_prios, + * device_api, + * device_info) + * @endcode{.cogeno.py} + */ +/** @code{.cogeno.ins}@endcode */ -#endif /* CONFIG_SPI_3 */ +/* + * @code{.jinja2gen} + * {% import 'devicedeclare.jinja2' as spi_stm32 %} + * + * {% set config_struct_body = """ + * .spi = (SPI_TypeDef *)0x{{ '%x'|format(data['device'].get_property('reg/0/address/0')) }}U, + * .pclken.bus = {{ data['device'].get_property('clocks/0/bus') }}, + * .pclken.enr = {{ data['device'].get_property('clocks/0/bits') }}, + * #ifdef CONFIG_SPI_STM32_INTERRUPT + * .irq_config = {{ irq_config_function_name }}, + * #endif """ + * %} + * + * {% set data_struct_body = """ + * SPI_CONTEXT_INIT_LOCK({{ data_struct_name }}, ctx), + * SPI_CONTEXT_INIT_SYNC({{ data_struct_name }}, ctx), """ + * %} + * + * {% set params = { 'irq_flag' : 'CONFIG_SPI_STM32_INTERRUPT', + * 'irq_func' : 'spi_stm32_isr', + * 'config_struct' : 'spi_stm32_config', + * 'data_struct' : 'spi_stm32_data', + * 'api_struct' : 'api_funcs', + * 'init_function' : 'spi_stm32_init', + * 'config_struct_body' : config_struct_body, + * 'data_struct_body' : data_struct_body } %} + * + * {{ spi_stm32.device(data, ['st,stm32-spi', 'st,stm32-spi-fifo'], params) }} + * @endcode{.jinja2gen} + */ +/** @code{.jinja2ins}@endcode */ +#endif /* 0 */ diff --git a/drivers/spi/spi_ll_stm32.h b/drivers/spi/spi_ll_stm32.h index d809dc1c1fe1..4d09c632d68f 100644 --- a/drivers/spi/spi_ll_stm32.h +++ b/drivers/spi/spi_ll_stm32.h @@ -15,7 +15,7 @@ struct spi_stm32_config { struct stm32_pclken pclken; SPI_TypeDef *spi; #ifdef CONFIG_SPI_STM32_INTERRUPT - irq_config_func_t irq_config; + irq_config_func_t config_irq; #endif }; diff --git a/drivers/spi/spi_ll_stm32_jinja2.h.jinja2 b/drivers/spi/spi_ll_stm32_jinja2.h.jinja2 new file mode 100644 index 000000000000..132dda9971e6 --- /dev/null +++ b/drivers/spi/spi_ll_stm32_jinja2.h.jinja2 @@ -0,0 +1,26 @@ +{% import 'devicedeclare.jinja2' as spi_stm32 %} + +{% set config_struct_body = """ + .spi = (SPI_TypeDef *)0x{{ '%x'|format(data['device'].get_property('reg/0/address/0')) }}U, + .pclken.bus = {{ data['device'].get_property('clocks/0/bus') }}, + .pclken.enr = {{ data['device'].get_property('clocks/0/bits') }}, +#ifdef CONFIG_SPI_STM32_INTERRUPT + .irq_config = {{ irq_config_function_name }}, +#endif """ +%} + +{% set data_struct_body = """ + SPI_CONTEXT_INIT_LOCK({{ data_struct_name }}, ctx), + SPI_CONTEXT_INIT_SYNC({{ data_struct_name }}, ctx), """ +%} + +{% set params = { 'irq_flag' : 'CONFIG_SPI_STM32_INTERRUPT', + 'irq_func' : 'spi_stm32_isr', + 'config_struct' : 'spi_stm32_config', + 'data_struct' : 'spi_stm32_data', + 'api_struct' : 'api_funcs', + 'init_function' : 'spi_stm32_init', + 'config_struct_body' : config_struct_body, + 'data_struct_body' : data_struct_body } %} + +{{ spi_stm32.device(data, ['st,stm32-spi', 'st,stm32-spi-fifo'], params) }} diff --git a/drivers/spi/spi_ll_stm32_python.h.py b/drivers/spi/spi_ll_stm32_python.h.py new file mode 100644 index 000000000000..8f88826bdc80 --- /dev/null +++ b/drivers/spi/spi_ll_stm32_python.h.py @@ -0,0 +1,42 @@ +cogeno.import_module('devicedeclare') + +device_configs = ['CONFIG_SPI_{}'.format(x) for x in range(1, 7)] +driver_names = ['SPI_{}'.format(x) for x in range(1, 7)] +device_inits = 'spi_stm32_init' +device_levels = 'POST_KERNEL' +device_prios = 'CONFIG_SPI_INIT_PRIORITY' +device_api = 'spi_stm32_driver_api' +device_info = \ +""" +#if CONFIG_SPI_STM32_INTERRUPT +DEVICE_DECLARE(${device-name}); +static void ${device-config-irq}(struct device *dev) +{ + IRQ_CONNECT(${interrupts/0/irq}, ${interrupts/0/priority}, \\ + spi_stm32_isr, \\ + DEVICE_GET(${device-name}), 0); + irq_enable(${interrupts/0/irq}); +} +#endif +static const struct spi_stm32_config ${device-config-info} = { + .spi = (SPI_TypeDef *)${reg/0/address/0}, + .pclken.bus = ${clocks/0/bus}, + .pclken.enr = ${clocks/0/bits}, +#if CONFIG_SPI_STM32_INTERRUPT + .config_irq = ${device-config-irq}, +#endif +}; +static struct spi_stm32_data ${device-data} = { + SPI_CONTEXT_INIT_LOCK(${device-data}, ctx), + SPI_CONTEXT_INIT_SYNC(${device-data}, ctx), +}; +""" + +devicedeclare.device_declare_multi( \ + device_configs, + driver_names, + device_inits, + device_levels, + device_prios, + device_api, + device_info) diff --git a/dts/arm/st/f0/stm32f0.dtsi b/dts/arm/st/f0/stm32f0.dtsi index 51a89b341796..1696b2b414b2 100644 --- a/dts/arm/st/f0/stm32f0.dtsi +++ b/dts/arm/st/f0/stm32f0.dtsi @@ -154,7 +154,7 @@ #address-cells = <1>; #size-cells = <0>; reg = <0x40013000 0x400>; - clocks = <&rcc STM32_CLOCK_BUS_APB2 0x00001000>; + clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00001000>; interrupts = <25 3>; status = "disabled"; label = "SPI_1"; diff --git a/dts/bindings/spi/spi.yaml b/dts/bindings/spi/spi.yaml index 6f65da7db42f..1e5b3ee1f8c7 100644 --- a/dts/bindings/spi/spi.yaml +++ b/dts/bindings/spi/spi.yaml @@ -41,6 +41,9 @@ properties: type: compound category: optional generation: define, use-prop-name - - + pinctrl-\d+: + type: array + category: optional + description: pinmux information for SCK, MOSI, MISO + generation: define ... diff --git a/dts/bindings/spi/st,stm32-spi-fifo.yaml b/dts/bindings/spi/st,stm32-spi-fifo.yaml index aa9ae3042d89..f9a221aa55fb 100644 --- a/dts/bindings/spi/st,stm32-spi-fifo.yaml +++ b/dts/bindings/spi/st,stm32-spi-fifo.yaml @@ -24,6 +24,12 @@ properties: generation: define category: required + clocks: + type: array + category: required + description: required clocks + generation: define + interrupts: type: array category: required From 14efbdc5970210f0dbbdb4bebab438c242cf9865 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Thu, 7 Jun 2018 08:38:07 +0200 Subject: [PATCH 08/13] drivers: serial: stm32: use code generation for driver instantiation Replace device instances code by code generation solution. Extend binding to allow DTS extraction for device info. Signed-off-by: Bobby Noelte --- drivers/serial/CMakeLists.txt | 2 +- drivers/serial/uart_stm32.c | 170 ++++++++++------------------------ 2 files changed, 50 insertions(+), 122 deletions(-) diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 5a732dd58be4..f3c36c4d50d4 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -20,7 +20,7 @@ zephyr_library_sources_if_kconfig(uart_qmsi.c) zephyr_library_sources_if_kconfig(uart_sam.c) zephyr_library_sources_if_kconfig(usart_sam.c) zephyr_library_sources_if_kconfig(uart_stellaris.c) -zephyr_library_sources_if_kconfig(uart_stm32.c) +zephyr_library_sources_codegen_ifdef(CONFIG_UART_STM32 uart_stm32.c) zephyr_library_sources_if_kconfig(uart_sam0.c) zephyr_library_sources_if_kconfig(usart_mcux_lpc.c) zephyr_library_sources_if_kconfig(uart_psoc6.c) diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index a274e8cfec8c..e06d9d10846e 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2016 Open-RnD Sp. z o.o. * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2018 Bobby Noelte. * * SPDX-License-Identifier: Apache-2.0 */ @@ -363,125 +364,52 @@ static int uart_stm32_init(struct device *dev) return 0; } - -#ifdef CONFIG_UART_INTERRUPT_DRIVEN -#define STM32_UART_IRQ_HANDLER_DECL(name) \ - static void uart_stm32_irq_config_func_##name(struct device *dev) -#define STM32_UART_IRQ_HANDLER_FUNC(name) \ - .irq_config_func = uart_stm32_irq_config_func_##name, -#define STM32_UART_IRQ_HANDLER(name) \ -static void uart_stm32_irq_config_func_##name(struct device *dev) \ -{ \ - IRQ_CONNECT(DT_##name##_IRQ, \ - DT_UART_STM32_##name##_IRQ_PRI, \ - uart_stm32_isr, DEVICE_GET(uart_stm32_##name), \ - 0); \ - irq_enable(DT_##name##_IRQ); \ -} -#else -#define STM32_UART_IRQ_HANDLER_DECL(name) -#define STM32_UART_IRQ_HANDLER_FUNC(name) -#define STM32_UART_IRQ_HANDLER(name) -#endif - -#define STM32_UART_INIT(name) \ -STM32_UART_IRQ_HANDLER_DECL(name); \ - \ -static const struct uart_stm32_config uart_stm32_cfg_##name = { \ - .uconf = { \ - .base = (u8_t *)DT_UART_STM32_##name##_BASE_ADDRESS,\ - STM32_UART_IRQ_HANDLER_FUNC(name) \ - }, \ - .pclken = { .bus = DT_UART_STM32_##name##_CLOCK_BUS, \ - .enr = DT_UART_STM32_##name##_CLOCK_BITS \ - }, \ - .baud_rate = DT_UART_STM32_##name##_BAUD_RATE \ -}; \ - \ -static struct uart_stm32_data uart_stm32_data_##name = { \ -}; \ - \ -DEVICE_AND_API_INIT(uart_stm32_##name, DT_UART_STM32_##name##_NAME, \ - &uart_stm32_init, \ - &uart_stm32_data_##name, &uart_stm32_cfg_##name, \ - PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ - &uart_stm32_driver_api); \ - \ -STM32_UART_IRQ_HANDLER(name) - - -#ifdef CONFIG_UART_STM32_PORT_1 -STM32_UART_INIT(USART_1) -#endif /* CONFIG_UART_STM32_PORT_1 */ - -#ifdef CONFIG_UART_STM32_PORT_2 -STM32_UART_INIT(USART_2) -#endif /* CONFIG_UART_STM32_PORT_2 */ - -#ifdef CONFIG_UART_STM32_PORT_3 -STM32_UART_INIT(USART_3) -#endif /* CONFIG_UART_STM32_PORT_3 */ - -#ifdef CONFIG_UART_STM32_PORT_6 -STM32_UART_INIT(USART_6) -#endif /* CONFIG_UART_STM32_PORT_6 */ - -/* - * STM32F0 and STM32L0 series differ from other STM32 series by some - * peripheral names (UART vs USART). - */ -#if defined(CONFIG_SOC_SERIES_STM32F0X) || defined(CONFIG_SOC_SERIES_STM32L0X) - -#ifdef CONFIG_UART_STM32_PORT_4 -STM32_UART_INIT(USART_4) -#endif /* CONFIG_UART_STM32_PORT_4 */ - -#ifdef CONFIG_UART_STM32_PORT_5 -STM32_UART_INIT(USART_5) -#endif /* CONFIG_UART_STM32_PORT_5 */ - -/* Following devices are not available in L0 series (for now) - * But keeping them simplifies ifdefery and won't harm +/** + * @code{.cogeno.py} + * cogeno.import_module('devicedeclare') + * + * device_configs = ['CONFIG_UART_STM32_PORT_{}'.format(x) \ + * for x in range(1, 11)] + \ + * ['CONFIG_UART_STM32_LPUART_{}'.format(x) \ + * for x in range(1, 2)] + * driver_names = ['UART_{}'.format(x) for x in range(1, 11)] + \ + * ['LPUART_{}'.format(x) for x in range(1, 2)] + * device_inits = 'uart_stm32_init' + * device_levels = 'PRE_KERNEL_1' + * device_prios = 'CONFIG_KERNEL_INIT_PRIORITY_DEVICE' + * device_api = 'uart_stm32_driver_api' + * device_info = \ + * """ + * #if CONFIG_UART_INTERRUPT_DRIVEN + * DEVICE_DECLARE(${device-name}); + * static void ${device-config-irq}(struct device *dev) + * { + * IRQ_CONNECT(${interrupts/0/irq}, ${interrupts/0/priority}, \\ + * uart_stm32_isr, \\ + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/0/irq}); + * } + * #endif + * static const struct uart_stm32_config ${device-config-info} = { + * .uconf.base = (u8_t *)${reg/0/address/0}, + * #ifdef CONFIG_UART_INTERRUPT_DRIVEN + * .uconf.irq_config_func = ${device-config-irq}, + * #endif + * .pclken.bus = ${clocks/0/bus}, + * .pclken.enr = ${clocks/0/bits}, + * .baud_rate = ${current-speed}, + * }; + * static struct uart_stm32_data ${device-data}; + * """ + * + * devicedeclare.device_declare_multi( \ + * device_configs, + * driver_names, + * device_inits, + * device_levels, + * device_prios, + * device_api, + * device_info) + * @endcode{.cogeno.py} */ - -#ifdef CONFIG_UART_STM32_PORT_7 -STM32_UART_INIT(USART_7) -#endif /* CONFIG_UART_STM32_PORT_7 */ - -#ifdef CONFIG_UART_STM32_PORT_8 -STM32_UART_INIT(USART_8) -#endif /* CONFIG_UART_STM32_PORT_8 */ - -#else - -#ifdef CONFIG_UART_STM32_PORT_4 -STM32_UART_INIT(UART_4) -#endif /* CONFIG_UART_STM32_PORT_4 */ - -#ifdef CONFIG_UART_STM32_PORT_5 -STM32_UART_INIT(UART_5) -#endif /* CONFIG_UART_STM32_PORT_5 */ - -#ifdef CONFIG_UART_STM32_PORT_7 -STM32_UART_INIT(UART_7) -#endif /* CONFIG_UART_STM32_PORT_7 */ - -#ifdef CONFIG_UART_STM32_PORT_8 -STM32_UART_INIT(UART_8) -#endif /* CONFIG_UART_STM32_PORT_8 */ - -#ifdef CONFIG_UART_STM32_PORT_9 -STM32_UART_INIT(UART_9) -#endif /* CONFIG_UART_STM32_PORT_9 */ - -#ifdef CONFIG_UART_STM32_PORT_10 -STM32_UART_INIT(UART_10) -#endif /* CONFIG_UART_STM32_PORT_10 */ - -#endif - -#if defined(CONFIG_SOC_SERIES_STM32L4X) || defined(CONFIG_SOC_SERIES_STM32L0X) -#ifdef CONFIG_UART_STM32_LPUART_1 -STM32_UART_INIT(LPUART_1) -#endif /* CONFIG_UART_STM32_LPUART_1 */ -#endif +/** @code{.cogeno.ins}@endcode */ From 67ad9203436757d6adc460118a6c0886ebecdc31 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Tue, 19 Jun 2018 22:45:27 +0200 Subject: [PATCH 09/13] drivers: can: stm32: use code generation for driver instantiation Replace device instances code by code generation solution. Extend binding to allow DTS extract for device info. Correct DTS to be used in CAN driver. Signed-off-by: Bobby Noelte --- drivers/can/CMakeLists.txt | 2 +- drivers/can/stm32_can.c | 111 ++++++++++++++++++++--------------- dts/arm/st/f0/stm32f091.dtsi | 15 +++++ dts/bindings/can/can.yaml | 5 ++ 4 files changed, 84 insertions(+), 49 deletions(-) diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 7b683babc3de..b32e73dfd316 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -1,2 +1,2 @@ -zephyr_sources_ifdef(CONFIG_CAN_STM32 stm32_can.c) +zephyr_sources_codegen_ifdef(CONFIG_CAN_STM32 stm32_can.c) zephyr_sources_ifdef(CONFIG_USERSPACE can_handlers.c) diff --git a/drivers/can/stm32_can.c b/drivers/can/stm32_can.c index d1dae4303ae8..4c2ed29048a5 100644 --- a/drivers/can/stm32_can.c +++ b/drivers/can/stm32_can.c @@ -841,51 +841,66 @@ static const struct can_driver_api can_api_funcs = { .detach = can_stm32_detach }; -#ifdef CONFIG_CAN_1 - -static void config_can_1_irq(CAN_TypeDef *can); - -static const struct can_stm32_config can_stm32_cfg_1 = { - .can = (CAN_TypeDef *)DT_CAN_1_BASE_ADDRESS, - .bus_speed = DT_CAN_1_BUS_SPEED, - .swj = DT_CAN_1_SJW, - .prop_bs1 = DT_CAN_1_PROP_SEG_PHASE_SEG1, - .bs2 = DT_CAN_1_PHASE_SEG2, - .pclken = { - .enr = DT_CAN_1_CLOCK_BITS, - .bus = DT_CAN_1_CLOCK_BUS, - }, - .config_irq = config_can_1_irq -}; - -static struct can_stm32_data can_stm32_dev_data_1; - -DEVICE_AND_API_INIT(can_stm32_1, DT_CAN_1_NAME, &can_stm32_init, - &can_stm32_dev_data_1, &can_stm32_cfg_1, - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &can_api_funcs); - -static void config_can_1_irq(CAN_TypeDef *can) -{ - LOG_DBG("Enable CAN1 IRQ"); -#ifdef CONFIG_SOC_SERIES_STM32F0X - IRQ_CONNECT(DT_CAN_1_IRQ, DT_CAN_1_IRQ_PRIORITY, can_stm32_isr, - DEVICE_GET(can_stm32_1), 0); - irq_enable(DT_CAN_1_IRQ); -#else - IRQ_CONNECT(DT_CAN_1_IRQ_RX0, DT_CAN_1_IRQ_PRIORITY, - can_stm32_rx_isr, DEVICE_GET(can_stm32_1), 0); - irq_enable(DT_CAN_1_IRQ_RX0); - - IRQ_CONNECT(DT_CAN_1_IRQ_TX, DT_CAN_1_IRQ_PRIORITY, - can_stm32_tx_isr, DEVICE_GET(can_stm32_1), 0); - irq_enable(DT_CAN_1_IRQ_TX); - - IRQ_CONNECT(DT_CAN_1_IRQ_SCE, DT_CAN_1_IRQ_PRIORITY, - can_stm32_tx_isr, DEVICE_GET(can_stm32_1), 0); - irq_enable(DT_CAN_1_IRQ_SCE); -#endif - can->IER |= CAN_IT_TME | CAN_IT_ERR | CAN_IT_FMP0 | CAN_IT_FMP1; -} - -#endif /*CONFIG_CAN_1*/ +/** + * @code{.cogeno.py} + * cogeno.import_module('devicedeclare') + * + * device_configs = ['CONFIG_CAN_1', ] + * driver_names = ['CAN_1', ] + * device_inits = 'can_stm32_init' + * device_levels = 'POST_KERNEL' + * device_prios = 'CONFIG_KERNEL_INIT_PRIORITY_DEVICE' + * device_api = 'can_api_funcs' + * device_info = \ + * """ + * DEVICE_DECLARE(${device-name}); + * static void ${device-config-irq}(CAN_TypeDef *can) + * { + * LOG_DBG("Enable ${driver-name} IRQ"); + * #ifdef CONFIG_SOC_SERIES_STM32F0X + * IRQ_CONNECT(${interrupts/0/irq}, ${interrupts/0/priority},\\ + * can_stm32_isr, + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/0/irq}); + * #else + * IRQ_CONNECT(${interrupts/rx0/irq}, ${interrupts/rx0/priority}, \\ + * can_stm32_rx_isr, + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/rx0/irq}); + * IRQ_CONNECT(${interrupts/tx/irq}, ${interrupts/tx/priority}, \\ + * can_stm32_tx_isr, + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/tx/irq}); + * IRQ_CONNECT(${interrupts/sce/irq}, ${interrupts/sce/priority}, \\ + * can_stm32_tx_isr, + * DEVICE_GET(${device-name}), 0); + * irq_enable(${interrupts/sce/irq}); + * #endif + * can->IER |= CAN_IT_TME | CAN_IT_ERR | CAN_IT_FMP0 | CAN_IT_FMP1; + * } + * static const struct can_stm32_config ${device-config-info} = { + * .can = (CAN_TypeDef *)${reg/0/address/0}, + * .bus_speed = ${bus-speed}, + * .swj = ${sjw}, + * .prop_bs1 = ${prop_seg_phase_seg1}, + * .bs2 = ${phase_seg2}, + * .pclken = { + * .enr = ${clocks/0/bits}, + * .bus = ${clocks/0/bus}, + * }, + * .config_irq = ${device-config-irq}, + * }; + * static struct can_stm32_data ${device-data}; + * """ + * + * devicedeclare.device_declare_multi( \ + * device_configs, + * driver_names, + * device_inits, + * device_levels, + * device_prios, + * device_api, + * device_info) + * @endcode{.cogeno.py} + */ +/** @code{.cogeno.ins}@endcode */ diff --git a/dts/arm/st/f0/stm32f091.dtsi b/dts/arm/st/f0/stm32f091.dtsi index ad6faf0c06b2..e6fd0cf300bf 100644 --- a/dts/arm/st/f0/stm32f091.dtsi +++ b/dts/arm/st/f0/stm32f091.dtsi @@ -46,5 +46,20 @@ label = "GPIOE"; }; }; + + can1: can@40006400 { + compatible = "st,stm32-can"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40006400 0x400>; + interrupts = <30 0>; + clocks = <&rcc STM32_CLOCK_BUS_APB1 0x02000000>; + status = "disabled"; + label = "CAN_1"; + bus-speed = <250000>; + sjw = <1>; + prop_seg_phase_seg1 = <5>; + phase_seg2 = <6>; + }; }; }; diff --git a/dts/bindings/can/can.yaml b/dts/bindings/can/can.yaml index aaa8ea4339bc..d5463ed31d69 100644 --- a/dts/bindings/can/can.yaml +++ b/dts/bindings/can/can.yaml @@ -27,6 +27,11 @@ properties: category: required description: Human readable string describing the device (used by Zephyr for API name) generation: define + clocks: + type: array + category: required + description: Clock gate information + generation: define bus-speed: type: int category: required From 08fb2e9d1eab5c11e81105531f18693e180c474e Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sun, 29 Jul 2018 13:09:34 +0200 Subject: [PATCH 10/13] drivers: pwm: stm32: use code generation for driver instantiation Replace device instances code by code generation solution. Extend binding to allow DTS extraction for device info. Adapt samples. Signed-off-by: Bobby Noelte --- drivers/pwm/CMakeLists.txt | 2 +- drivers/pwm/pwm_stm32.c | 175 +++++++---------------------- drivers/pwm/pwm_stm32.h | 2 + dts/arm/st/f0/stm32f0.dtsi | 18 +-- dts/bindings/pwm/pwm-client.yaml | 32 ++++++ dts/bindings/pwm/pwm.yaml | 12 ++ dts/bindings/pwm/st,stm32-pwm.yaml | 24 +++- include/dt-bindings/pwm/pwm.h | 12 ++ samples/basic/blink_led/src/main.c | 4 +- samples/basic/fade_led/src/main.c | 4 +- 10 files changed, 131 insertions(+), 154 deletions(-) create mode 100644 dts/bindings/pwm/pwm-client.yaml create mode 100644 include/dt-bindings/pwm/pwm.h diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index 1565e78ddff9..d1244eef3f38 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -3,7 +3,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_PWM_PCA9685 pwm_pca9685.c) zephyr_library_sources_ifdef(CONFIG_PWM_DW pwm_dw.c) zephyr_library_sources_ifdef(CONFIG_PWM_QMSI pwm_qmsi.c) -zephyr_library_sources_ifdef(CONFIG_PWM_STM32 pwm_stm32.c) +zephyr_library_sources_codegen_ifdef(CONFIG_PWM_STM32 pwm_stm32.c) zephyr_library_sources_ifdef(CONFIG_PWM_NRF5_SW pwm_nrf5_sw.c) zephyr_library_sources_ifdef(CONFIG_PWM_NRFX pwm_nrfx.c) zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_FTM pwm_mcux_ftm.c) diff --git a/drivers/pwm/pwm_stm32.c b/drivers/pwm/pwm_stm32.c index f2b16c45cff1..902820a4b88e 100644 --- a/drivers/pwm/pwm_stm32.c +++ b/drivers/pwm/pwm_stm32.c @@ -202,138 +202,43 @@ static int pwm_stm32_init(struct device *dev) return 0; } -#define PWM_DEVICE_INIT_STM32(n, apb, grp, prescaler) \ - static struct pwm_stm32_data pwm_stm32_dev_data_ ## n = { \ - /* Default case */ \ - .pwm_prescaler = prescaler, \ - }; \ - \ - static const struct pwm_stm32_config pwm_stm32_dev_cfg_ ## n = { \ - .pwm_base = TIM ## n ## _BASE, \ - .pclken = { .bus = STM32_CLOCK_BUS_ ## apb, \ - .enr = LL_##apb##_##grp##_PERIPH_TIM##n }, \ - }; \ - \ - DEVICE_AND_API_INIT(pwm_stm32_ ## n, \ - DT_PWM_STM32_ ## n ## _DEV_NAME, \ - pwm_stm32_init, \ - &pwm_stm32_dev_data_ ## n, \ - &pwm_stm32_dev_cfg_ ## n, \ - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,\ - &pwm_stm32_drv_api_funcs); - -#ifdef CONFIG_PWM_STM32_1 -/* 16-bit advanced-control timer */ -#ifdef CONFIG_SOC_SERIES_STM32F0X -PWM_DEVICE_INIT_STM32(1, APB1, GRP2, DT_PWM_STM32_1_PRESCALER) -#else -PWM_DEVICE_INIT_STM32(1, APB2, GRP1, DT_PWM_STM32_1_PRESCALER) -#endif /*CONFIG_SOC_SERIES_STM32F0X */ -#endif /* CONFIG_PWM_STM32_1 */ - -#ifdef CONFIG_PWM_STM32_2 -/* 32-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(2, APB1, GRP1, DT_PWM_STM32_2_PRESCALER) -#endif /* CONFIG_PWM_STM32_2 */ - -#ifdef CONFIG_PWM_STM32_3 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(3, APB1, GRP1, DT_PWM_STM32_3_PRESCALER) -#endif /* CONFIG_PWM_STM32_3 */ - -#ifdef CONFIG_PWM_STM32_4 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(4, APB1, GRP1, DT_PWM_STM32_4_PRESCALER) -#endif /* CONFIG_PWM_STM32_4 */ - -#ifdef CONFIG_PWM_STM32_5 -/* 32-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(5, APB1, GRP1, DT_PWM_STM32_5_PRESCALER) -#endif /* CONFIG_PWM_STM32_5 */ - -#ifdef CONFIG_PWM_STM32_6 -/* 16-bit basic timer */ -PWM_DEVICE_INIT_STM32(6, APB1, GRP1, DT_PWM_STM32_6_PRESCALER) -#endif /* CONFIG_PWM_STM32_6 */ - -#ifdef CONFIG_PWM_STM32_7 -/* 16-bit basic timer */ -PWM_DEVICE_INIT_STM32(7, APB1, GRP1, DT_PWM_STM32_7_PRESCALER) -#endif /* CONFIG_PWM_STM32_7 */ - -#ifdef CONFIG_PWM_STM32_8 -/* 16-bit advanced-control timer */ -PWM_DEVICE_INIT_STM32(8, APB2, GRP1, DT_PWM_STM32_8_PRESCALER) -#endif /* CONFIG_PWM_STM32_8 */ - -#ifdef CONFIG_PWM_STM32_9 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(9, APB2, GRP1, DT_PWM_STM32_9_PRESCALER) -#endif /* CONFIG_PWM_STM32_9 */ - -#ifdef CONFIG_PWM_STM32_10 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(10, APB2, GRP1, DT_PWM_STM32_10_PRESCALER) -#endif /* CONFIG_PWM_STM32_10 */ - -#ifdef CONFIG_PWM_STM32_11 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(11, APB2, GRP1, DT_PWM_STM32_11_PRESCALER) -#endif /* CONFIG_PWM_STM32_11 */ - -#ifdef CONFIG_PWM_STM32_12 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(12, APB1, GRP1, DT_PWM_STM32_12_PRESCALER) -#endif /* CONFIG_PWM_STM32_12 */ - -#ifdef CONFIG_PWM_STM32_13 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(13, APB1, GRP1, DT_PWM_STM32_13_PRESCALER) -#endif /* CONFIG_PWM_STM32_13 */ - -#ifdef CONFIG_PWM_STM32_14 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(14, APB1, GRP1, DT_PWM_STM32_14_PRESCALER) -#endif /* CONFIG_PWM_STM32_14 */ - -#ifdef CONFIG_PWM_STM32_15 -/* 16-bit general-purpose timer */ -#ifdef CONFIG_SOC_SERIES_STM32F0X -PWM_DEVICE_INIT_STM32(15, APB1, GRP2, DT_PWM_STM32_15_PRESCALER) -#else -PWM_DEVICE_INIT_STM32(15, APB2, GRP1, DT_PWM_STM32_15_PRESCALER) -#endif /* CONFIG_SOC_SERIES_STM32F0X */ -#endif /* CONFIG_PWM_STM32_15 */ - -#ifdef CONFIG_PWM_STM32_16 -/* 16-bit general-purpose timer */ -#ifdef CONFIG_SOC_SERIES_STM32F0X -PWM_DEVICE_INIT_STM32(16, APB1, GRP2, DT_PWM_STM32_16_PRESCALER) -#else -PWM_DEVICE_INIT_STM32(16, APB2, GRP1, DT_PWM_STM32_16_PRESCALER) -#endif /* CONFIG_SOC_SERIES_STM32F0X */ -#endif /* CONFIG_PWM_STM32_16 */ - -#ifdef CONFIG_PWM_STM32_17 -/* 16-bit general-purpose timer */ -#ifdef CONFIG_SOC_SERIES_STM32F0X -PWM_DEVICE_INIT_STM32(17, APB1, GRP2, DT_PWM_STM32_17_PRESCALER) -#else -PWM_DEVICE_INIT_STM32(17, APB2, GRP1, DT_PWM_STM32_17_PRESCALER) -#endif /* CONFIG_SOC_SERIES_STM32F0X */ -#endif /* CONFIG_PWM_STM32_17 */ - -#ifdef CONFIG_PWM_STM32_18 -/* 16-bit advanced timer */ -PWM_DEVICE_INIT_STM32(18, APB1, GRP1, DT_PWM_STM32_18_PRESCALER) -#endif /* CONFIG_PWM_STM32_18 */ - -#ifdef CONFIG_PWM_STM32_19 -/* 16-bit general-purpose timer */ -PWM_DEVICE_INIT_STM32(19, APB2, GRP1, DT_PWM_STM32_19_PRESCALER) -#endif /* CONFIG_PWM_STM32_19 */ - -#ifdef CONFIG_PWM_STM32_20 -/* 16-bit advanced timer */ -PWM_DEVICE_INIT_STM32(20, APB2, GRP1, DT_PWM_STM32_20_PRESCALER) -#endif /* CONFIG_PWM_STM32_20 */ +/** + * @code{.cogeno.py} + * cogeno.import_module('devicedeclare') + * + * device_configs = ['CONFIG_PWM_STM32_{}'.format(x) for x in range(1, 21)] + * driver_names = ['PWM_{}'.format(x) for x in range(1, 21)] + * device_inits = 'pwm_stm32_init' + * device_levels = 'POST_KERNEL' + * device_prios = 'CONFIG_KERNEL_INIT_PRIORITY_DEVICE' + * device_api = 'pwm_stm32_drv_api_funcs' + * device_info = \ + * """ + * static const struct pwm_stm32_config ${device-config-info} = { + * .pwm_base = (u32_t)${${parent-device}:reg/0/address/0}, + * .pclken.bus = ${${parent-device}:clocks/0/bus}, + * .pclken.enr = ${${parent-device}:clocks/0/bits}, + * .pwm_num_chan = ${st,pwm-num-chan}, + * .capture_num_chan = ${st,capture-num-chan}, + * }; + * static struct pwm_stm32_data ${device-data} = { + * .pwm_prescaler = ${st,prescaler}, + * }; + * """ + * device_defaults = { + * 'st,pwm-num-chan' : 0, + * 'st,capture-num-chan' : 0, + * } + * + * devicedeclare.device_declare_multi( \ + * device_configs, + * driver_names, + * device_inits, + * device_levels, + * device_prios, + * device_api, + * device_info, + * device_defaults) + * @endcode{.cogeno.py} + */ +/** @code{.cogeno.ins}@endcode */ diff --git a/drivers/pwm/pwm_stm32.h b/drivers/pwm/pwm_stm32.h index 7d66685a80be..3f929b66a4a6 100644 --- a/drivers/pwm/pwm_stm32.h +++ b/drivers/pwm/pwm_stm32.h @@ -18,6 +18,8 @@ extern "C" { /** Configuration data */ struct pwm_stm32_config { u32_t pwm_base; + u8_t pwm_num_chan; + u8_t capture_num_chan; /* clock subsystem driving this peripheral */ struct stm32_pclken pclken; }; diff --git a/dts/arm/st/f0/stm32f0.dtsi b/dts/arm/st/f0/stm32f0.dtsi index 1696b2b414b2..790de776b917 100644 --- a/dts/arm/st/f0/stm32f0.dtsi +++ b/dts/arm/st/f0/stm32f0.dtsi @@ -9,6 +9,8 @@ #include #include #include +#include +#include / { cpus { @@ -167,7 +169,7 @@ status = "disabled"; label = "TIMERS_1"; - pwm { + timers1_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -183,7 +185,7 @@ status = "disabled"; label = "TIMERS_3"; - pwm { + timers3_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -199,7 +201,7 @@ status = "disabled"; label = "TIMERS_6"; - pwm { + timers6_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -215,7 +217,7 @@ status = "disabled"; label = "TIMERS_7"; - pwm { + timers7_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -231,7 +233,7 @@ status = "disabled"; label = "TIMERS_14"; - pwm { + timers14_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -247,7 +249,7 @@ status = "disabled"; label = "TIMERS_15"; - pwm { + timers15_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -263,7 +265,7 @@ status = "disabled"; label = "TIMERS_16"; - pwm { + timers16_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -279,7 +281,7 @@ status = "disabled"; label = "TIMERS_17"; - pwm { + timers17_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; diff --git a/dts/bindings/pwm/pwm-client.yaml b/dts/bindings/pwm/pwm-client.yaml new file mode 100644 index 000000000000..4a67f050692b --- /dev/null +++ b/dts/bindings/pwm/pwm-client.yaml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2018 Bobby Noelte +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: PWM Client Base Structure +id: pwm-client +version: 0.1 + +description: > + This binding gives the base structures for all pwm clients. + +properties: + + pwms: + type: array + category: required + description: > + List of phandle and pwm specifiers, one set for each pwm + input to the device. Note - The number of pwm specifiers is + given by the pwm-cells property of the pwm provider. + generation: define + + pwm-names: + type: array + category: optional + description: > + List of strings to label each of the PWM devices sorted in the same + order as the pwms property. + generation: define +... diff --git a/dts/bindings/pwm/pwm.yaml b/dts/bindings/pwm/pwm.yaml index 05e96636f236..8ec8244013f6 100644 --- a/dts/bindings/pwm/pwm.yaml +++ b/dts/bindings/pwm/pwm.yaml @@ -17,6 +17,12 @@ properties: description: compatible strings generation: define + "#pwm-cells": + type: int + category: required + description: > + Number of cells used to specify a PWM. + clocks: type: array category: required @@ -28,4 +34,10 @@ properties: category: required description: Human readable string describing the device (used by Zephyr for API name) generation: define + + pinctrl-\d+: + type: array + category: optional + description: pinmux information for PWMx output + generation: define ... diff --git a/dts/bindings/pwm/st,stm32-pwm.yaml b/dts/bindings/pwm/st,stm32-pwm.yaml index 67a0010cfe8d..a0f9d5c4241c 100644 --- a/dts/bindings/pwm/st,stm32-pwm.yaml +++ b/dts/bindings/pwm/st,stm32-pwm.yaml @@ -5,15 +5,15 @@ version: 0.1 description: > This binding gives a base representation of the STM32 PWM +inherits: + !include pwm.yaml + properties: compatible: constraint: "st,stm32-pwm" - label: - type: string - category: required - description: Human readable string describing the device (used by Zephyr for API name) - generation: define + "#pwm-cells": + constraint: 2 st,prescaler: type: int @@ -21,7 +21,19 @@ properties: description: Clock prescaler at the input of the timer generation: define -"#cells": + st,pwm-num-chan: + type: int + category: optional + description: Number of available PWM channels. Default is 0. + generation: define + + st,capture-num-chan: + type: int + category: optional + description: Number of available Capture channels. Default is 0. + generation: define + +"#pwm-cells": - channel # period in terms of nanoseconds - period diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h new file mode 100644 index 000000000000..f3248a77279c --- /dev/null +++ b/include/dt-bindings/pwm/pwm.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2018 Bobby Noelte + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PWM_DTS_H +#define _PWM_DTS_H + +#define PWM_POLARITY_INVERTED (1 << 0) + +#endif /* _PWM_DTS_H */ diff --git a/samples/basic/blink_led/src/main.c b/samples/basic/blink_led/src/main.c index a805558a442d..0cfd95cf18ca 100644 --- a/samples/basic/blink_led/src/main.c +++ b/samples/basic/blink_led/src/main.c @@ -18,10 +18,10 @@ #if defined(CONFIG_SOC_STM32F401XE) || defined(CONFIG_SOC_STM32F412ZG) || \ defined(CONFIG_SOC_STM32F413XX) || defined(CONFIG_SOC_STM32L476XG) || \ defined(CONFIG_SOC_STM32F407XG) || defined(CONFIG_SOC_STM32F302X8) -#define PWM_DRIVER DT_PWM_STM32_2_DEV_NAME +#define PWM_DRIVER "PWM_2" #define PWM_CHANNEL 1 #elif CONFIG_SOC_STM32F103XB -#define PWM_DRIVER DT_PWM_STM32_1_DEV_NAME +#define PWM_DRIVER "PWM_1" #define PWM_CHANNEL 1 #elif defined(CONFIG_SOC_QUARK_SE_C1000) || defined(CONFIG_SOC_QUARK_D2000) #define PWM_DRIVER CONFIG_PWM_QMSI_DEV_NAME diff --git a/samples/basic/fade_led/src/main.c b/samples/basic/fade_led/src/main.c index 96087a28b232..eef90af6dad4 100644 --- a/samples/basic/fade_led/src/main.c +++ b/samples/basic/fade_led/src/main.c @@ -16,10 +16,10 @@ #include #if defined(CONFIG_SOC_STM32F401XE) || defined(CONFIG_SOC_STM32L476XG) -#define PWM_DRIVER DT_PWM_STM32_2_DEV_NAME +#define PWM_DRIVER "PWM_2" #define PWM_CHANNEL 1 #elif CONFIG_SOC_STM32F103XB -#define PWM_DRIVER DT_PWM_STM32_1_DEV_NAME +#define PWM_DRIVER "PWM_1" #define PWM_CHANNEL 1 #elif defined(CONFIG_SOC_QUARK_SE_C1000) || defined(CONFIG_SOC_QUARK_D2000) #define PWM_DRIVER CONFIG_PWM_QMSI_DEV_NAME From 3cebfd3706dd22c98fa523094cfa075f99abec2d Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sat, 11 Aug 2018 07:57:10 +0200 Subject: [PATCH 11/13] drivers: interrupt: stm32: use code generation for driver instantiation Replace device instances code by code generation solution. Add binding to allow DTS extract for device info. Add DTS to be used in EXTI driver. Signed-off-by: Bobby Noelte --- drivers/interrupt_controller/CMakeLists.txt | 2 +- drivers/interrupt_controller/exti_stm32.c | 158 ++++++++++-------- dts/arm/st/f0/stm32f0.dtsi | 9 + dts/arm/st/f1/stm32f1.dtsi | 9 + dts/arm/st/f2/stm32f2.dtsi | 9 + dts/arm/st/f3/stm32f3.dtsi | 9 + dts/arm/st/f4/stm32f4.dtsi | 9 + dts/arm/st/f7/stm32f7.dtsi | 9 + dts/arm/st/l0/stm32l0.dtsi | 9 + dts/arm/st/l4/stm32l4.dtsi | 9 + .../interrupt-controller/st,stm32-exti.yaml | 50 ++++++ 11 files changed, 211 insertions(+), 71 deletions(-) create mode 100644 dts/bindings/interrupt-controller/st,stm32-exti.yaml diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index 82fb5e7941e5..4b94e7be5833 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -6,6 +6,6 @@ zephyr_sources_ifdef(CONFIG_MVIC mvic.c) zephyr_sources_ifdef(CONFIG_PIC_DISABLE i8259.c) zephyr_sources_ifdef(CONFIG_PLIC plic.c) zephyr_sources_ifdef(CONFIG_SHARED_IRQ shared_irq.c) -zephyr_sources_ifdef(CONFIG_SOC_FAMILY_STM32 exti_stm32.c) +zephyr_sources_codegen_ifdef(CONFIG_EXTI_STM32 exti_stm32.c) zephyr_sources_ifdef(CONFIG_CAVS_ICTL cavs_ictl.c) zephyr_sources_ifdef(CONFIG_DW_ICTL dw_ictl.c) diff --git a/drivers/interrupt_controller/exti_stm32.c b/drivers/interrupt_controller/exti_stm32.c index 1417982ebcf2..cfdd2e420990 100644 --- a/drivers/interrupt_controller/exti_stm32.c +++ b/drivers/interrupt_controller/exti_stm32.c @@ -436,17 +436,34 @@ static int stm32_exti_init(struct device *dev) return 0; } -static struct stm32_exti_data exti_data; -DEVICE_INIT(exti_stm32, STM32_EXTI_NAME, stm32_exti_init, - &exti_data, NULL, - PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); +/** + * @code{.cogeno.py} + * cogeno.import_module('devicedeclare') + * + * device_data = [ 'stm32_exti_data', \ + * """ + * #define EXTI_STM32_DEVICE_NAME ${device-name} + * """] + * + * devicedeclare.device_declare( \ + * ['st,stm32-exti', ], + * 'CONFIG_KERNEL_INIT_PRIORITY_DEVICE', + * 'PRE_KERNEL_1', + * None, + * 'stm32_exti_init', + * None, + * device_data, + * None) + * @endcode{.cogeno.py} + */ +/** @code{.cogeno.ins}@endcode */ /** * @brief set & unset for the interrupt callbacks */ void stm32_exti_set_callback(int line, stm32_exti_callback_t cb, void *arg) { - struct device *dev = DEVICE_GET(exti_stm32); + struct device *dev = DEVICE_GET(EXTI_STM32_DEVICE_NAME); struct stm32_exti_data *data = dev->driver_data; __ASSERT(data->cb[line].cb == NULL, @@ -458,7 +475,7 @@ void stm32_exti_set_callback(int line, stm32_exti_callback_t cb, void *arg) void stm32_exti_unset_callback(int line) { - struct device *dev = DEVICE_GET(exti_stm32); + struct device *dev = DEVICE_GET(EXTI_STM32_DEVICE_NAME); struct stm32_exti_data *data = dev->driver_data; data->cb[line].cb = NULL; @@ -475,266 +492,267 @@ static void __stm32_exti_connect_irqs(struct device *dev) #ifdef CONFIG_SOC_SERIES_STM32F0X IRQ_CONNECT(EXTI0_1_IRQn, CONFIG_EXTI_STM32_EXTI1_0_IRQ_PRI, - __stm32_exti_isr_0_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_3_IRQn, CONFIG_EXTI_STM32_EXTI3_2_IRQ_PRI, - __stm32_exti_isr_2_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_15_IRQn, CONFIG_EXTI_STM32_EXTI15_4_IRQ_PRI, - __stm32_exti_isr_4_15, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4_15, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif CONFIG_SOC_SERIES_STM32F1X IRQ_CONNECT(EXTI0_IRQn, CONFIG_EXTI_STM32_EXTI0_IRQ_PRI, - __stm32_exti_isr_0, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI1_IRQn, CONFIG_EXTI_STM32_EXTI1_IRQ_PRI, - __stm32_exti_isr_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_IRQn, CONFIG_EXTI_STM32_EXTI2_IRQ_PRI, - __stm32_exti_isr_2, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI3_IRQn, CONFIG_EXTI_STM32_EXTI3_IRQ_PRI, - __stm32_exti_isr_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_IRQn, CONFIG_EXTI_STM32_EXTI4_IRQ_PRI, - __stm32_exti_isr_4, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI9_5_IRQn, CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI, - __stm32_exti_isr_9_5, DEVICE_GET(exti_stm32), + __stm32_exti_isr_9_5, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI15_10_IRQn, CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI, - __stm32_exti_isr_15_10, DEVICE_GET(exti_stm32), + __stm32_exti_isr_15_10, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif CONFIG_SOC_SERIES_STM32F2X IRQ_CONNECT(EXTI0_IRQn, CONFIG_EXTI_STM32_EXTI0_IRQ_PRI, - __stm32_exti_isr_0, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI1_IRQn, CONFIG_EXTI_STM32_EXTI1_IRQ_PRI, - __stm32_exti_isr_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_IRQn, CONFIG_EXTI_STM32_EXTI2_IRQ_PRI, - __stm32_exti_isr_2, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI3_IRQn, CONFIG_EXTI_STM32_EXTI3_IRQ_PRI, - __stm32_exti_isr_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_IRQn, CONFIG_EXTI_STM32_EXTI4_IRQ_PRI, - __stm32_exti_isr_4, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI9_5_IRQn, CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI, - __stm32_exti_isr_9_5, DEVICE_GET(exti_stm32), + __stm32_exti_isr_9_5, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI15_10_IRQn, CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI, - __stm32_exti_isr_15_10, DEVICE_GET(exti_stm32), + __stm32_exti_isr_15_10, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(PVD_IRQn, CONFIG_EXTI_STM32_PVD_IRQ_PRI, - __stm32_exti_isr_16, DEVICE_GET(exti_stm32), + __stm32_exti_isr_16, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(RTC_Alarm_IRQn, CONFIG_EXTI_STM32_RTC_ALARM_IRQ_PRI, - __stm32_exti_isr_17, DEVICE_GET(exti_stm32), + __stm32_exti_isr_17, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(OTG_FS_WKUP_IRQn, CONFIG_EXTI_STM32_OTG_FS_WKUP_IRQ_PRI, - __stm32_exti_isr_18, DEVICE_GET(exti_stm32), + __stm32_exti_isr_18, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(TAMP_STAMP_IRQn, CONFIG_EXTI_STM32_TAMP_STAMP_IRQ_PRI, - __stm32_exti_isr_21, DEVICE_GET(exti_stm32), + __stm32_exti_isr_21, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(RTC_WKUP_IRQn, CONFIG_EXTI_STM32_RTC_WKUP_IRQ_PRI, - __stm32_exti_isr_22, DEVICE_GET(exti_stm32), + __stm32_exti_isr_22, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif CONFIG_SOC_SERIES_STM32F3X IRQ_CONNECT(EXTI0_IRQn, CONFIG_EXTI_STM32_EXTI0_IRQ_PRI, - __stm32_exti_isr_0, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI1_IRQn, CONFIG_EXTI_STM32_EXTI1_IRQ_PRI, - __stm32_exti_isr_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_TSC_IRQn, CONFIG_EXTI_STM32_EXTI2_IRQ_PRI, - __stm32_exti_isr_2, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI3_IRQn, CONFIG_EXTI_STM32_EXTI3_IRQ_PRI, - __stm32_exti_isr_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_IRQn, CONFIG_EXTI_STM32_EXTI4_IRQ_PRI, - __stm32_exti_isr_4, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI9_5_IRQn, CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI, - __stm32_exti_isr_9_5, DEVICE_GET(exti_stm32), + __stm32_exti_isr_9_5, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI15_10_IRQn, CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI, - __stm32_exti_isr_15_10, DEVICE_GET(exti_stm32), + __stm32_exti_isr_15_10, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif CONFIG_SOC_SERIES_STM32F4X IRQ_CONNECT(EXTI0_IRQn, CONFIG_EXTI_STM32_EXTI0_IRQ_PRI, - __stm32_exti_isr_0, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI1_IRQn, CONFIG_EXTI_STM32_EXTI1_IRQ_PRI, - __stm32_exti_isr_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_IRQn, CONFIG_EXTI_STM32_EXTI2_IRQ_PRI, - __stm32_exti_isr_2, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI3_IRQn, CONFIG_EXTI_STM32_EXTI3_IRQ_PRI, - __stm32_exti_isr_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_IRQn, CONFIG_EXTI_STM32_EXTI4_IRQ_PRI, - __stm32_exti_isr_4, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI9_5_IRQn, CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI, - __stm32_exti_isr_9_5, DEVICE_GET(exti_stm32), + __stm32_exti_isr_9_5, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI15_10_IRQn, CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI, - __stm32_exti_isr_15_10, DEVICE_GET(exti_stm32), + __stm32_exti_isr_15_10, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(PVD_IRQn, CONFIG_EXTI_STM32_PVD_IRQ_PRI, - __stm32_exti_isr_16, DEVICE_GET(exti_stm32), + __stm32_exti_isr_16, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(RTC_Alarm_IRQn, CONFIG_EXTI_STM32_RTC_ALARM_IRQ_PRI, - __stm32_exti_isr_17, DEVICE_GET(exti_stm32), + __stm32_exti_isr_17, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(OTG_FS_WKUP_IRQn, CONFIG_EXTI_STM32_OTG_FS_WKUP_IRQ_PRI, - __stm32_exti_isr_18, DEVICE_GET(exti_stm32), + __stm32_exti_isr_18, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(TAMP_STAMP_IRQn, CONFIG_EXTI_STM32_TAMP_STAMP_IRQ_PRI, - __stm32_exti_isr_21, DEVICE_GET(exti_stm32), + __stm32_exti_isr_21, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(RTC_WKUP_IRQn, CONFIG_EXTI_STM32_RTC_WKUP_IRQ_PRI, - __stm32_exti_isr_22, DEVICE_GET(exti_stm32), + __stm32_exti_isr_22, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif CONFIG_SOC_SERIES_STM32F7X IRQ_CONNECT(EXTI0_IRQn, CONFIG_EXTI_STM32_EXTI0_IRQ_PRI, - __stm32_exti_isr_0, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI1_IRQn, CONFIG_EXTI_STM32_EXTI1_IRQ_PRI, - __stm32_exti_isr_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_IRQn, CONFIG_EXTI_STM32_EXTI2_IRQ_PRI, - __stm32_exti_isr_2, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI3_IRQn, CONFIG_EXTI_STM32_EXTI3_IRQ_PRI, - __stm32_exti_isr_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_IRQn, CONFIG_EXTI_STM32_EXTI4_IRQ_PRI, - __stm32_exti_isr_4, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI9_5_IRQn, CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI, - __stm32_exti_isr_9_5, DEVICE_GET(exti_stm32), + __stm32_exti_isr_9_5, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI15_10_IRQn, CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI, - __stm32_exti_isr_15_10, DEVICE_GET(exti_stm32), + __stm32_exti_isr_15_10, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(PVD_IRQn, CONFIG_EXTI_STM32_PVD_IRQ_PRI, - __stm32_exti_isr_16, DEVICE_GET(exti_stm32), + __stm32_exti_isr_16, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(RTC_Alarm_IRQn, CONFIG_EXTI_STM32_RTC_ALARM_IRQ_PRI, - __stm32_exti_isr_17, DEVICE_GET(exti_stm32), + __stm32_exti_isr_17, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(OTG_FS_WKUP_IRQn, CONFIG_EXTI_STM32_OTG_FS_WKUP_IRQ_PRI, - __stm32_exti_isr_18, DEVICE_GET(exti_stm32), + __stm32_exti_isr_18, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(TAMP_STAMP_IRQn, CONFIG_EXTI_STM32_TAMP_STAMP_IRQ_PRI, - __stm32_exti_isr_21, DEVICE_GET(exti_stm32), + __stm32_exti_isr_21, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(RTC_WKUP_IRQn, CONFIG_EXTI_STM32_RTC_WKUP_IRQ_PRI, - __stm32_exti_isr_22, DEVICE_GET(exti_stm32), + __stm32_exti_isr_22, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(LPTIM1_IRQn, CONFIG_EXTI_STM32_LPTIM1_IRQ_PRI, - __stm32_exti_isr_23, DEVICE_GET(exti_stm32), + __stm32_exti_isr_23, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif defined(CONFIG_SOC_SERIES_STM32L0X) IRQ_CONNECT(EXTI0_1_IRQn, CONFIG_EXTI_STM32_EXTI1_0_IRQ_PRI, - __stm32_exti_isr_0_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_3_IRQn, CONFIG_EXTI_STM32_EXTI3_2_IRQ_PRI, - __stm32_exti_isr_2_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_15_IRQn, CONFIG_EXTI_STM32_EXTI15_4_IRQ_PRI, - __stm32_exti_isr_4_15, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4_15, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #elif defined(CONFIG_SOC_SERIES_STM32L4X) IRQ_CONNECT(EXTI0_IRQn, CONFIG_EXTI_STM32_EXTI0_IRQ_PRI, - __stm32_exti_isr_0, DEVICE_GET(exti_stm32), + __stm32_exti_isr_0, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI1_IRQn, CONFIG_EXTI_STM32_EXTI1_IRQ_PRI, - __stm32_exti_isr_1, DEVICE_GET(exti_stm32), + __stm32_exti_isr_1, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI2_IRQn, CONFIG_EXTI_STM32_EXTI2_IRQ_PRI, - __stm32_exti_isr_2, DEVICE_GET(exti_stm32), + __stm32_exti_isr_2, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI3_IRQn, CONFIG_EXTI_STM32_EXTI3_IRQ_PRI, - __stm32_exti_isr_3, DEVICE_GET(exti_stm32), + __stm32_exti_isr_3, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI4_IRQn, CONFIG_EXTI_STM32_EXTI4_IRQ_PRI, - __stm32_exti_isr_4, DEVICE_GET(exti_stm32), + __stm32_exti_isr_4, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI9_5_IRQn, CONFIG_EXTI_STM32_EXTI9_5_IRQ_PRI, - __stm32_exti_isr_9_5, DEVICE_GET(exti_stm32), + __stm32_exti_isr_9_5, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); IRQ_CONNECT(EXTI15_10_IRQn, CONFIG_EXTI_STM32_EXTI15_10_IRQ_PRI, - __stm32_exti_isr_15_10, DEVICE_GET(exti_stm32), + __stm32_exti_isr_15_10, DEVICE_GET(EXTI_STM32_DEVICE_NAME), 0); #endif } + diff --git a/dts/arm/st/f0/stm32f0.dtsi b/dts/arm/st/f0/stm32f0.dtsi index 790de776b917..bc9d0a0c6d06 100644 --- a/dts/arm/st/f0/stm32f0.dtsi +++ b/dts/arm/st/f0/stm32f0.dtsi @@ -55,6 +55,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40010400 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40010400 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@48000000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/f1/stm32f1.dtsi b/dts/arm/st/f1/stm32f1.dtsi index 9414a66a50f2..0959e18cf642 100644 --- a/dts/arm/st/f1/stm32f1.dtsi +++ b/dts/arm/st/f1/stm32f1.dtsi @@ -41,6 +41,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40010400 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40010400 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@40010800 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/f2/stm32f2.dtsi b/dts/arm/st/f2/stm32f2.dtsi index 64c2cc21760b..587d1a2654c5 100644 --- a/dts/arm/st/f2/stm32f2.dtsi +++ b/dts/arm/st/f2/stm32f2.dtsi @@ -52,6 +52,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40013C00 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40013C00 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@40020000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/f3/stm32f3.dtsi b/dts/arm/st/f3/stm32f3.dtsi index f590bbb4a8e0..a34106641cb3 100644 --- a/dts/arm/st/f3/stm32f3.dtsi +++ b/dts/arm/st/f3/stm32f3.dtsi @@ -51,6 +51,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40010400 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40010400 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@48000000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/f4/stm32f4.dtsi b/dts/arm/st/f4/stm32f4.dtsi index 63c39592397c..ff695c117f51 100644 --- a/dts/arm/st/f4/stm32f4.dtsi +++ b/dts/arm/st/f4/stm32f4.dtsi @@ -53,6 +53,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40013C00 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40013C00 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@40020000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/f7/stm32f7.dtsi b/dts/arm/st/f7/stm32f7.dtsi index 272b150fd8dc..6b1ac40a275a 100644 --- a/dts/arm/st/f7/stm32f7.dtsi +++ b/dts/arm/st/f7/stm32f7.dtsi @@ -42,6 +42,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40013C00 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40013C00 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@40020000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/l0/stm32l0.dtsi b/dts/arm/st/l0/stm32l0.dtsi index 6b230961aff5..f1f48da181fc 100644 --- a/dts/arm/st/l0/stm32l0.dtsi +++ b/dts/arm/st/l0/stm32l0.dtsi @@ -53,6 +53,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40010400 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40010400 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@50000000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/arm/st/l4/stm32l4.dtsi b/dts/arm/st/l4/stm32l4.dtsi index 2c39cc5cc25d..27f6fcd58790 100644 --- a/dts/arm/st/l4/stm32l4.dtsi +++ b/dts/arm/st/l4/stm32l4.dtsi @@ -55,6 +55,15 @@ label = "STM32_CLK_RCC"; }; + exti: interrupt-controller@40010400 { + compatible = "st,stm32-exti"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x40010400 0x400>; + label = "EXTI_STM32"; + status = "ok"; + }; + pinctrl: pin-controller@48000000 { compatible = "st,stm32-pinmux"; #address-cells = <1>; diff --git a/dts/bindings/interrupt-controller/st,stm32-exti.yaml b/dts/bindings/interrupt-controller/st,stm32-exti.yaml new file mode 100644 index 000000000000..3c3bb22a72d4 --- /dev/null +++ b/dts/bindings/interrupt-controller/st,stm32-exti.yaml @@ -0,0 +1,50 @@ +--- +title: STM32 External Interrupt Controller +type: st,stm32-exti +version: 0.1 + +description: > + This binding describes the STM32 External Interrupt Controller. + +properties: + compatible: + category: required + type: string + description: compatible strings + constraint: "st,stm32-exti" + generation: define + + interrupt-controller: + type: string + category: required + description: device controller identification + generation: define + + label: + type: string + category: required + description: Human readable string describing the device (used by Zephyr for API name) + generation: define + + reg: + category: required + type: int + description: mmio register space + generation: define + + "#interrupt-cells": + type: int + category: required + description: > + Specifies the number of cells to encode an interrupt specifier. + constraint: 2 + + interrupts: + type: array + category: optional + description: > + interrupts references to primary interrupt controller + (only needed for exti controller with multiple exti under + same parent interrupt - st,stm32-exti and st,stm32h7-exti) + generation: define +... From be75e68de5b8ea73fe0108ca266fd89b4fc5b0f3 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Tue, 20 Feb 2018 20:58:54 +0100 Subject: [PATCH 12/13] tests: drivers: spi: support NUCLEO F0x boards The NUCLEO FO91RC, F070RB, F030R8 boards use SPI_1 for loopback test. Signed-off-by: Bobby Noelte --- tests/drivers/spi/spi_loopback/boards/nucleo_f030r8.conf | 1 + tests/drivers/spi/spi_loopback/boards/nucleo_f070rb.conf | 1 + tests/drivers/spi/spi_loopback/boards/nucleo_f091rc.conf | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 tests/drivers/spi/spi_loopback/boards/nucleo_f030r8.conf create mode 100644 tests/drivers/spi/spi_loopback/boards/nucleo_f070rb.conf create mode 100644 tests/drivers/spi/spi_loopback/boards/nucleo_f091rc.conf diff --git a/tests/drivers/spi/spi_loopback/boards/nucleo_f030r8.conf b/tests/drivers/spi/spi_loopback/boards/nucleo_f030r8.conf new file mode 100644 index 000000000000..9059fb218dbd --- /dev/null +++ b/tests/drivers/spi/spi_loopback/boards/nucleo_f030r8.conf @@ -0,0 +1 @@ +CONFIG_SPI_LOOPBACK_DRV_NAME="SPI_1" diff --git a/tests/drivers/spi/spi_loopback/boards/nucleo_f070rb.conf b/tests/drivers/spi/spi_loopback/boards/nucleo_f070rb.conf new file mode 100644 index 000000000000..9059fb218dbd --- /dev/null +++ b/tests/drivers/spi/spi_loopback/boards/nucleo_f070rb.conf @@ -0,0 +1 @@ +CONFIG_SPI_LOOPBACK_DRV_NAME="SPI_1" diff --git a/tests/drivers/spi/spi_loopback/boards/nucleo_f091rc.conf b/tests/drivers/spi/spi_loopback/boards/nucleo_f091rc.conf new file mode 100644 index 000000000000..9c7722658592 --- /dev/null +++ b/tests/drivers/spi/spi_loopback/boards/nucleo_f091rc.conf @@ -0,0 +1,3 @@ +CONFIG_SPI_LOOPBACK_DRV_NAME="SPI_1" +CONFIG_SPI_LOOPBACK_SLOW_FREQ=550000 +CONFIG_SPI_LOOPBACK_FAST_FREQ=2000000 From 635029890b38d60de75489ede0c806a839e578a4 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Mon, 26 Nov 2018 15:28:31 +0100 Subject: [PATCH 13/13] spi: sam: fix function and config naming Incorrect naming prevents compilation. Change names: - CONFIG_xx_NAME -> DT_xx_NAME - spi_sam0_xx -> spi_sam_xx Signed-off-by: Bobby Noelte --- drivers/spi/spi_sam.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi_sam.c b/drivers/spi/spi_sam.c index fda693985985..1d691379ad9c 100644 --- a/drivers/spi/spi_sam.c +++ b/drivers/spi/spi_sam.c @@ -395,15 +395,15 @@ static int spi_sam_transceive(struct device *dev, return err; } -static int spi_sam0_transceive_sync(struct device *dev, +static int spi_sam_transceive_sync(struct device *dev, const struct spi_config *config, const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs) { - struct spi_sam0_data *data = dev->driver_data; + struct spi_sam_data *data = dev->driver_data; spi_context_lock(&data->ctx, false, NULL); - return spi_sam0_transceive(dev, config, tx_bufs, rx_bufs); + return spi_sam_transceive(dev, config, tx_bufs, rx_bufs); } #ifdef CONFIG_SPI_ASYNC @@ -413,10 +413,10 @@ static int spi_sam_transceive_async(struct device *dev, const struct spi_buf_set *rx_bufs, struct k_poll_signal *async) { - struct spi_sam0_data *data = dev->driver_data; + struct spi_sam_data *data = dev->driver_data; spi_context_lock(&data->ctx, true, async); - return spi_sam0_transceive(dev, config, tx_bufs, rx_bufs); + return spi_sam_transceive(dev, config, tx_bufs, rx_bufs); } #endif /* CONFIG_SPI_ASYNC */ @@ -513,7 +513,7 @@ static const struct spi_driver_api spi_sam_driver_api = { SPI_CONTEXT_INIT_SYNC(spi_sam_dev_data_##n, ctx), \ }; \ DEVICE_AND_API_INIT(spi_sam_##n, \ - CONFIG_SPI_##n##_NAME, \ + DT_SPI_##n##_NAME, \ &spi_sam_init, &spi_sam_dev_data_##n, \ &spi_sam_config_##n, POST_KERNEL, \ CONFIG_SPI_INIT_PRIORITY, &spi_sam_driver_api)