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/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/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 { 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/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/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 */ 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/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) diff --git a/dts/arm/st/f0/stm32f0.dtsi b/dts/arm/st/f0/stm32f0.dtsi index 51a89b341796..bc9d0a0c6d06 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 { @@ -53,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>; @@ -154,7 +165,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"; @@ -167,7 +178,7 @@ status = "disabled"; label = "TIMERS_1"; - pwm { + timers1_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -183,7 +194,7 @@ status = "disabled"; label = "TIMERS_3"; - pwm { + timers3_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -199,7 +210,7 @@ status = "disabled"; label = "TIMERS_6"; - pwm { + timers6_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -215,7 +226,7 @@ status = "disabled"; label = "TIMERS_7"; - pwm { + timers7_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -231,7 +242,7 @@ status = "disabled"; label = "TIMERS_14"; - pwm { + timers14_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -247,7 +258,7 @@ status = "disabled"; label = "TIMERS_15"; - pwm { + timers15_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -263,7 +274,7 @@ status = "disabled"; label = "TIMERS_16"; - pwm { + timers16_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; @@ -279,7 +290,7 @@ status = "disabled"; label = "TIMERS_17"; - pwm { + timers17_pwm: pwm { compatible = "st,stm32-pwm"; status = "disabled"; st,prescaler = <10000>; 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/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/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 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 +... 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/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 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 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/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/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 +... 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) 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 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 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) 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