From 5735397b9b00f3f1f10b0c0c29a97e2154ca6d38 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 28 Aug 2019 09:30:23 -0500 Subject: [PATCH] kconfig: Add new functions to kconfigfunctions to use EDT Add a new set of functions that utilize EDT so we can move away from the generated .conf file. Signed-off-by: Kumar Gala --- cmake/dts.cmake | 1 + cmake/kconfig.cmake | 4 + doc/guides/kconfig/index.rst | 66 ++++----- scripts/kconfig/kconfigfunctions.py | 201 ++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 38 deletions(-) diff --git a/cmake/dts.cmake b/cmake/dts.cmake index 9704f124e04..612560a933a 100644 --- a/cmake/dts.cmake +++ b/cmake/dts.cmake @@ -14,6 +14,7 @@ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/generated) # See ~/zephyr/doc/dts set(GENERATED_DTS_BOARD_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/generated_dts_board_unfixed.h) set(GENERATED_DTS_BOARD_CONF ${PROJECT_BINARY_DIR}/include/generated/generated_dts_board.conf) +set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp) set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts) set_ifndef(DTS_COMMON_OVERLAYS ${ZEPHYR_BASE}/dts/common/common.dts) diff --git a/cmake/kconfig.cmake b/cmake/kconfig.cmake index e59a32e85d1..4543c34f5d3 100644 --- a/cmake/kconfig.cmake +++ b/cmake/kconfig.cmake @@ -39,6 +39,8 @@ set(ENV{SOC_DIR} ${SOC_DIR}) set(ENV{CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}) set(ENV{ARCH_DIR} ${ARCH_DIR}) set(ENV{GENERATED_DTS_BOARD_CONF} ${GENERATED_DTS_BOARD_CONF}) +set(ENV{DTS_POST_CPP} ${DTS_POST_CPP}) +set(ENV{DTS_ROOT_BINDINGS} ${DTS_ROOT_BINDINGS}) # Allow out-of-tree users to add their own Kconfig python frontend # targets by appending targets to the CMake list @@ -77,6 +79,8 @@ foreach(kconfig_target ZEPHYR_TOOLCHAIN_VARIANT=${ZEPHYR_TOOLCHAIN_VARIANT} ARCH_DIR=$ENV{ARCH_DIR} GENERATED_DTS_BOARD_CONF=${GENERATED_DTS_BOARD_CONF} + DTS_POST_CPP=${DTS_POST_CPP} + DTS_ROOT_BINDINGS=${DTS_ROOT_BINDINGS} ${PYTHON_EXECUTABLE} ${EXTRA_KCONFIG_TARGET_COMMAND_FOR_${kconfig_target}} ${KCONFIG_ROOT} diff --git a/doc/guides/kconfig/index.rst b/doc/guides/kconfig/index.rst index 00757099dda..fb02a722b8b 100644 --- a/doc/guides/kconfig/index.rst +++ b/doc/guides/kconfig/index.rst @@ -815,62 +815,52 @@ from the device tree. Device Tree Related Functions ============================= +See the Python docstrings in ``scripts/kconfig/kconfigfunctions.py`` for more +details on the functions. + .. code-block:: none - dt_int_val(kconf, _, name, unit): - This function looks up 'name' in the DTS generated "conf" style database - (generated_dts_board.conf in /zephyr/include/generated/) - and if it's found it will return the value as an decimal integer. The - function will divide the value based on 'unit': - None No division - 'k' or 'K' divide by 1024 (1 << 10) - 'm' or 'M' divide by 1,048,576 (1 << 20) - 'g' or 'G' divide by 1,073,741,824 (1 << 30) - - dt_hex_val(kconf, _, name, unit): - This function looks up 'name' in the DTS generated "conf" style database - (generated_dts_board.conf in /zephyr/include/generated/) - and if it's found it will return the value as an hex integer. The - function will divide the value based on 'unit': - None No division - 'k' or 'K' divide by 1024 (1 << 10) - 'm' or 'M' divide by 1,048,576 (1 << 20) - 'g' or 'G' divide by 1,073,741,824 (1 << 30) - - dt_str_val(kconf, _, name): - This function looks up 'name' in the DTS generated "conf" style database - (generated_dts_board.conf in /zephyr/include/generated/) - and if it's found it will return the value as string. if it's not found we - return an empty string. + dt_chosen_reg_addr(kconf, _, chosen, index=0, unit=None): + dt_chosen_reg_size(kconf, _, chosen, index=0, unit=None): + dt_node_reg_addr(kconf, _, path, index=0, unit=None): + dt_node_reg_size(kconf, _, path, index=0, unit=None): + dt_compat_enabled(kconf, _, compat): + dt_node_has_bool_prop(kconf, _, path, prop): Example Usage -============= +------------- -The following example shows the usage of the ``dt_int_val`` function: +The following example shows the usage of the ``dt_node_reg_addr`` function. +This function will take a path to a device tree node and register the register +address of that node: .. code-block:: none - boards/arm/mimxrt1020_evk/Kconfig.defconfig + boards/riscv/hifive1_revb/Kconfig.defconfig - config FLASH_SIZE - default $(dt_int_val,DT_NXP_IMX_FLEXSPI_402A8000_SIZE_1,K) + config FLASH_BASE_ADDRESS + default $(dt_node_reg_addr,/soc/spi@10014000,1) -In this example if we examine the generated generated_dts_board.conf file -as part of the Zephyr build we'd find the following entry: +In this example if we examine the dts file for the board: .. code-block:: none - DT_NXP_IMX_FLEXSPI_402A8000_SIZE_1=8388608 + spi0: spi@10014000 { + compatible = "sifive,spi0"; + reg = <0x10014000 0x1000 0x20010000 0x3c0900>; + reg-names = "control", "mem"; + ... + }; -The ``dt_int_val`` will search the generated_dts_board.conf that is derived from -the dts for the board and match the ``DT_NXP_IMX_FLEXSPI_402A8000_SIZE_1`` entry. -The function than will than scale the value by ``1024``. This effective causes +The ``dt_node_reg_addr`` will search the dts file for a node at the path +``/soc/spi@10014000``. The function than will extract the register address +at the index 1. This effective gets the value of ``0x20010000`` and causes the above to look like: .. code-block:: none - config FLASH_SIZE - default 8192 + config FLASH_BASE_ADDRESS + default 0x20010000 Other resources diff --git a/scripts/kconfig/kconfigfunctions.py b/scripts/kconfig/kconfigfunctions.py index a2dc124206c..938dd324f16 100644 --- a/scripts/kconfig/kconfigfunctions.py +++ b/scripts/kconfig/kconfigfunctions.py @@ -5,6 +5,12 @@ # SPDX-License-Identifier: Apache-2.0 import os +import sys + +ZEPHYR_BASE = os.environ.get("ZEPHYR_BASE") +sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/dts")) + +import edtlib # Types we support # 'string', 'int', 'hex', 'bool' @@ -13,6 +19,15 @@ doc_mode = os.environ.get('KCONFIG_DOC_MODE') == "1" dt_defines = {} if not doc_mode: + DTS_POST_CPP = os.environ["DTS_POST_CPP"] + BINDINGS_DIR = os.environ.get("DTS_ROOT_BINDINGS") + + # if a board port doesn't use DTS than these might not be set + if os.path.isfile(DTS_POST_CPP) and BINDINGS_DIR is not None: + edt = edtlib.EDT(DTS_POST_CPP, [BINDINGS_DIR]) + else: + edt = None + # The env var 'GENERATED_DTS_BOARD_CONF' must be set unless we are in # doc mode GENERATED_DTS_BOARD_CONF = os.environ['GENERATED_DTS_BOARD_CONF'] @@ -91,8 +106,194 @@ def dt_str_val(kconf, _, name): return dt_defines[name].strip('"') + +def dt_chosen_label(kconf, _, chosen): + """ + This function takes a 'chosen' property and treats that property as a path + to a EDT device. If it finds a EDT device, it will look to see if that + device has a "label" property and return the value of that "label", if not + we return an empty string. + """ + if doc_mode or edt is None: + return "" + + dev = edt.chosen_dev(chosen) + if not dev: + return "" + + if "label" not in dev.props: + return "" + + return dev.props["label"].val + + +def _dev_reg_addr(dev, index, unit): + if not dev: + return "0x0" + + if not dev.regs: + return "0x0" + + if int(index) >= len(dev.regs): + return "0x0" + + return hex(dev.regs[int(index)].addr >> _dt_units_to_scale(unit)) + + +def _dev_reg_size(dev, index, unit): + if not dev: + return "0" + + if not dev.regs: + return "0" + + if int(index) >= len(dev.regs): + return "0" + + return str(dev.regs[int(index)].size >> _dt_units_to_scale(unit)) + + +def dt_chosen_reg_addr(kconf, _, chosen, index=0, unit=None): + """ + This function takes a 'chosen' property and treats that property as a path + to a EDT device. If it finds a EDT device, it will look to see if that + device has a register at the give 'index' and return the address value of + that reg, if not we return 0. + + The function will divide the value based on 'unit': + None No division + 'k' or 'K' divide by 1024 (1 << 10) + 'm' or 'M' divide by 1,048,576 (1 << 20) + 'g' or 'G' divide by 1,073,741,824 (1 << 30) + """ + if doc_mode or edt is None: + return "0x0" + + dev = edt.chosen_dev(chosen) + + return _dev_reg_addr(dev, index, unit) + + +def dt_chosen_reg_size(kconf, _, chosen, index=0, unit=None): + """ + This function takes a 'chosen' property and treats that property as a path + to a EDT device. If it finds a EDT device, it will look to see if that + device has a register at the give 'index' and return the size value of + that reg, if not we return 0. + + The function will divide the value based on 'unit': + None No division + 'k' or 'K' divide by 1024 (1 << 10) + 'm' or 'M' divide by 1,048,576 (1 << 20) + 'g' or 'G' divide by 1,073,741,824 (1 << 30) + """ + if doc_mode or edt is None: + return "0" + + dev = edt.chosen_dev(chosen) + + return _dev_reg_size(dev, index, unit) + + +def dt_node_reg_addr(kconf, _, path, index=0, unit=None): + """ + This function takes a 'path' and looks for a EDT device at that path. + If it finds a EDT device, it will look to see if that device has a + register at the give 'index' and return the address value of that reg, if + not we return 0. + + The function will divide the value based on 'unit': + None No division + 'k' or 'K' divide by 1024 (1 << 10) + 'm' or 'M' divide by 1,048,576 (1 << 20) + 'g' or 'G' divide by 1,073,741,824 (1 << 30) + """ + if doc_mode or edt is None: + return "0" + + try: + dev = edt.get_dev(path) + except edtlib.EDTError: + return "0" + + return _dev_reg_addr(dev, index, unit) + + +def dt_node_reg_size(kconf, _, path, index=0, unit=None): + """ + This function takes a 'path' and looks for a EDT device at that path. + If it finds a EDT device, it will look to see if that device has a + register at the give 'index' and return the size value of that reg, if + not we return 0. + + The function will divide the value based on 'unit': + None No division + 'k' or 'K' divide by 1024 (1 << 10) + 'm' or 'M' divide by 1,048,576 (1 << 20) + 'g' or 'G' divide by 1,073,741,824 (1 << 30) + """ + if doc_mode or edt is None: + return "0" + + try: + dev = edt.get_dev(path) + except edtlib.EDTError: + return "0" + + return _dev_reg_size(dev, index, unit) + + +def dt_node_has_bool_prop(kconf, _, path, prop): + """ + This function takes a 'path' and looks for a EDT device at that path. + If it finds a EDT device, it will look to see if that device has a + boolean property by the name of 'prop'. If the 'prop' exists it will + return "y" otherwise we return "n". + """ + if doc_mode or edt is None: + return "n" + + try: + dev = edt.get_dev(path) + except edtlib.EDTError: + return "n" + + if prop not in dev.props: + return "n" + + if dev.props[prop].type != "boolean": + return "n" + + if dev.props[prop].val: + return "y" + + return "n" + + +def dt_compat_enabled(kconf, _, compat): + """ + This function takes a 'compat' and returns "y" if we find an "enabled" + compatible device in the EDT otherwise we return "n" + """ + if doc_mode or edt is None: + return "n" + + for dev in edt.devices: + if compat in dev.compats and dev.enabled: + return "y" + + return "n" + + functions = { "dt_int_val": (dt_int_val, 1, 2), "dt_hex_val": (dt_hex_val, 1, 2), "dt_str_val": (dt_str_val, 1, 1), + "dt_compat_enabled": (dt_compat_enabled, 1, 1), + "dt_chosen_label": (dt_chosen_label, 1, 1), + "dt_chosen_reg_addr": (dt_chosen_reg_addr, 1, 3), + "dt_chosen_reg_size": (dt_chosen_reg_size, 1, 3), + "dt_node_reg_addr": (dt_node_reg_addr, 1, 3), + "dt_node_reg_size": (dt_node_reg_size, 1, 3), + "dt_node_has_bool_prop": (dt_node_has_bool_prop, 2, 2), }